aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLeonid <logvinov.leon@gmail.com>2018-01-19 20:39:57 +0800
committerGitHub <noreply@github.com>2018-01-19 20:39:57 +0800
commit6091f818da7cddcb4a22a573f7726ba9b76b398e (patch)
tree5769ca73d50c497e46e6c6c927700ae0fa00bec9
parenta45f9b480233d207fa5ba80a8e47cc3193c243d8 (diff)
parent9ccf63b44afe1ae6ccc2b9298ee35697d6be9e30 (diff)
downloaddexon-0x-contracts-6091f818da7cddcb4a22a573f7726ba9b76b398e.tar.gz
dexon-0x-contracts-6091f818da7cddcb4a22a573f7726ba9b76b398e.tar.zst
dexon-0x-contracts-6091f818da7cddcb4a22a573f7726ba9b76b398e.zip
Merge branch 'development' into feature/separate-deployer
-rw-r--r--README.md3
-rw-r--r--package.json2
-rw-r--r--packages/0x.js/CHANGELOG.md4
-rw-r--r--packages/0x.js/test/exchange_wrapper_test.ts47
-rw-r--r--packages/assert/src/index.ts2
-rw-r--r--packages/assert/test/assert_test.ts14
-rw-r--r--packages/website/public/images/social/discourse.pngbin0 -> 1454 bytes
-rw-r--r--packages/website/ts/blockchain.ts24
-rw-r--r--packages/website/ts/components/footer.tsx11
-rw-r--r--packages/website/ts/components/generate_order/new_token_form.tsx8
-rw-r--r--packages/website/ts/components/token_balances.tsx2
-rw-r--r--packages/website/ts/components/top_bar.tsx14
-rw-r--r--packages/website/ts/utils/configs.ts4
-rw-r--r--packages/website/ts/utils/constants.ts1
-rw-r--r--packages/website/ts/web3_wrapper.ts72
15 files changed, 147 insertions, 61 deletions
diff --git a/README.md b/README.md
index 0fc00ba84..79bfc31e1 100644
--- a/README.md
+++ b/README.md
@@ -27,12 +27,13 @@ This repository contains all the 0x developer tools written in TypeScript. Our h
| [`@0xproject/abi-gen`](/packages/abi-gen) | [![npm](https://img.shields.io/npm/v/@0xproject/abi-gen.svg)](https://www.npmjs.com/package/@0xproject/abi-gen) | Tool to generate TS wrappers from smart contract ABIs |
| [`@0xproject/assert`](/packages/assert) | [![npm](https://img.shields.io/npm/v/@0xproject/assert.svg)](https://www.npmjs.com/package/@0xproject/assert) | Type and schema assertions used by our packages |
| [`@0xproject/connect`](/packages/connect) | [![npm](https://img.shields.io/npm/v/@0xproject/connect.svg)](https://www.npmjs.com/package/@0xproject/connect) | A Javascript library for interacting with the standard relayer api |
+| [`@0xproject/dev-utils`](/packages/dev-utils) | [![npm](https://img.shields.io/npm/v/@0xproject/dev-utils.svg)](https://www.npmjs.com/package/@0xproject/dev-utils) | Dev utils to be shared across 0x projects and packages |
| [`@0xproject/json-schemas`](/packages/json-schemas) | [![npm](https://img.shields.io/npm/v/@0xproject/json-schemas.svg)](https://www.npmjs.com/package/@0xproject/json-schemas) | 0x-related json schemas |
| [`@0xproject/subproviders`](/packages/subproviders) | [![npm](https://img.shields.io/npm/v/@0xproject/subproviders.svg)](https://www.npmjs.com/package/@0xproject/subproviders) | Useful web3 subproviders (e.g LedgerSubprovider) |
| [`@0xproject/tslint-config`](/packages/tslint-config) | [![npm](https://img.shields.io/npm/v/@0xproject/tslint-config.svg)](https://www.npmjs.com/package/@0xproject/tslint-config) | Custom 0x development TSLint rules |
| [`@0xproject/types`](/packages/types) | [![npm](https://img.shields.io/npm/v/@0xproject/types.svg)](https://www.npmjs.com/package/@0xproject/types) | Shared type declarations |
| [`@0xproject/utils`](/packages/utils) | [![npm](https://img.shields.io/npm/v/@0xproject/utils.svg)](https://www.npmjs.com/package/@0xproject/utils) | Shared utilities |
-| [`@0xproject/web3-wrapper`](/packages/web3-wrapper) | [![npm](https://img.shields.io/npm/v/@0xproject/web3-wrapper.svg)](https://www.npmjs.com/package/@0xproject/web3-wrapper) | Web3 wrapper | |
+| [`@0xproject/web3-wrapper`](/packages/web3-wrapper) | [![npm](https://img.shields.io/npm/v/@0xproject/web3-wrapper.svg)](https://www.npmjs.com/package/@0xproject/web3-wrapper) | Web3 wrapper |
### Private Packages
diff --git a/package.json b/package.json
index d0e6966cd..dcb48c10d 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,7 @@
"mnemonic": "concert load couple harbor equip island argue ramp clarify fence smart topic"
},
"devDependencies": {
- "@0xproject/utils": "^0.1.0",
+ "@0xproject/utils": "^0.2.0",
"async-child-process": "^1.1.1",
"ethereumjs-testrpc": "^6.0.3",
"lerna": "^2.5.1",
diff --git a/packages/0x.js/CHANGELOG.md b/packages/0x.js/CHANGELOG.md
index 84997389a..1a09eaf28 100644
--- a/packages/0x.js/CHANGELOG.md
+++ b/packages/0x.js/CHANGELOG.md
@@ -1,5 +1,9 @@
# CHANGELOG
+## v0.30.1 - _January 18, 2018_
+
+ * Fix a bug allowing negative fill values (#212)
+
## v0.30.0 - _January 17, 2018_
* Add an error parameter to the order watcher callback (#312)
diff --git a/packages/0x.js/test/exchange_wrapper_test.ts b/packages/0x.js/test/exchange_wrapper_test.ts
index d2a2149a0..7e0ffd818 100644
--- a/packages/0x.js/test/exchange_wrapper_test.ts
+++ b/packages/0x.js/test/exchange_wrapper_test.ts
@@ -390,6 +390,29 @@ describe('ExchangeWrapper', () => {
).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
});
});
+ describe('negative fill amount', async () => {
+ let signedOrder: SignedOrder;
+ const negativeFillTakerAmount = new BigNumber(-100);
+ beforeEach(async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerTokenAddress,
+ takerTokenAddress,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ });
+ it('should not allow the exchange wrapper to fill if amount is negative', async () => {
+ return expect(
+ zeroEx.exchange.fillOrderAsync(
+ signedOrder,
+ negativeFillTakerAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ ),
+ ).to.be.rejected();
+ });
+ });
});
describe('#batchFillOrdersAsync', () => {
let signedOrder: SignedOrder;
@@ -498,6 +521,30 @@ describe('ExchangeWrapper', () => {
).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
});
});
+ describe('negative batch fill amount', async () => {
+ beforeEach(async () => {
+ const negativeFillTakerAmount = new BigNumber(-100);
+ orderFillBatch = [
+ {
+ signedOrder,
+ takerTokenFillAmount,
+ },
+ {
+ signedOrder: anotherSignedOrder,
+ takerTokenFillAmount: negativeFillTakerAmount,
+ },
+ ];
+ });
+ it('should not allow the exchange wrapper to batch fill if any amount is negative', async () => {
+ return expect(
+ zeroEx.exchange.batchFillOrdersAsync(
+ orderFillBatch,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ takerAddress,
+ ),
+ ).to.be.rejected();
+ });
+ });
});
describe('#fillOrdersUpTo', () => {
let signedOrder: SignedOrder;
diff --git a/packages/assert/src/index.ts b/packages/assert/src/index.ts
index 56f663780..7ad574ec7 100644
--- a/packages/assert/src/index.ts
+++ b/packages/assert/src/index.ts
@@ -12,6 +12,8 @@ export const assert = {
},
isValidBaseUnitAmount(variableName: string, value: BigNumber) {
assert.isBigNumber(variableName, value);
+ const isNegative = value.lessThan(0);
+ this.assert(!isNegative, `${variableName} cannot be a negative number, found value: ${value.toNumber()}`);
const hasDecimals = value.decimalPlaces() !== 0;
this.assert(
!hasDecimals,
diff --git a/packages/assert/test/assert_test.ts b/packages/assert/test/assert_test.ts
index ff337196d..b0fa398d6 100644
--- a/packages/assert/test/assert_test.ts
+++ b/packages/assert/test/assert_test.ts
@@ -22,6 +22,20 @@ describe('Assertions', () => {
invalidInputs.forEach(input => expect(assert.isBigNumber.bind(assert, variableName, input)).to.throw());
});
});
+ describe('#isValidBaseUnitAmount', () => {
+ it('should not throw for valid input', () => {
+ const validInputs = [new BigNumber(23), new BigNumber('45000000')];
+ validInputs.forEach(input =>
+ expect(assert.isValidBaseUnitAmount.bind(assert, variableName, input)).to.not.throw(),
+ );
+ });
+ it('should throw for invalid input', () => {
+ const invalidInputs = [0, undefined, new BigNumber(3.145), 3.145, new BigNumber(-400)];
+ invalidInputs.forEach(input =>
+ expect(assert.isValidBaseUnitAmount.bind(assert, variableName, input)).to.throw(),
+ );
+ });
+ });
describe('#isString', () => {
it('should not throw for valid input', () => {
const validInputs = ['hello', 'goodbye'];
diff --git a/packages/website/public/images/social/discourse.png b/packages/website/public/images/social/discourse.png
new file mode 100644
index 000000000..4bca3de0d
--- /dev/null
+++ b/packages/website/public/images/social/discourse.png
Binary files differ
diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts
index 1320fbeb9..5530701c0 100644
--- a/packages/website/ts/blockchain.ts
+++ b/packages/website/ts/blockchain.ts
@@ -368,14 +368,22 @@ export class Blockchain {
const [currBalance] = await this.getTokenBalanceAndAllowanceAsync(this._userAddress, token.address);
- this._zrxPollIntervalId = intervalUtils.setAsyncExcludingInterval(async () => {
- const [balance] = await this.getTokenBalanceAndAllowanceAsync(this._userAddress, token.address);
- if (!balance.eq(currBalance)) {
- this._dispatcher.replaceTokenBalanceByAddress(token.address, balance);
- clearInterval(this._zrxPollIntervalId);
+ this._zrxPollIntervalId = intervalUtils.setAsyncExcludingInterval(
+ async () => {
+ const [balance] = await this.getTokenBalanceAndAllowanceAsync(this._userAddress, token.address);
+ if (!balance.eq(currBalance)) {
+ this._dispatcher.replaceTokenBalanceByAddress(token.address, balance);
+ intervalUtils.clearAsyncExcludingInterval(this._zrxPollIntervalId);
+ delete this._zrxPollIntervalId;
+ }
+ },
+ 5000,
+ (err: Error) => {
+ utils.consoleLog(`Polling tokenBalance failed: ${err}`);
+ intervalUtils.clearAsyncExcludingInterval(this._zrxPollIntervalId);
delete this._zrxPollIntervalId;
- }
- }, 5000);
+ },
+ );
}
public async signOrderHashAsync(orderHash: string): Promise<SignatureData> {
utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
@@ -471,7 +479,7 @@ export class Blockchain {
this._web3Wrapper.updatePrevUserAddress(newUserAddress);
}
public destroy() {
- clearInterval(this._zrxPollIntervalId);
+ intervalUtils.clearAsyncExcludingInterval(this._zrxPollIntervalId);
this._web3Wrapper.destroy();
this._stopWatchingExchangeLogFillEvents();
}
diff --git a/packages/website/ts/components/footer.tsx b/packages/website/ts/components/footer.tsx
index 3346f2545..a0f1a0c96 100644
--- a/packages/website/ts/components/footer.tsx
+++ b/packages/website/ts/components/footer.tsx
@@ -13,7 +13,6 @@ interface FooterMenuItem {
title: string;
path?: string;
isExternal?: boolean;
- fileName?: string;
}
enum Sections {
@@ -56,25 +55,26 @@ const menuItemsBySection: MenuItemsBySection = {
title: 'Rocket.chat',
isExternal: true,
path: constants.URL_ZEROEX_CHAT,
- fileName: 'rocketchat.png',
},
{
title: 'Blog',
isExternal: true,
path: constants.URL_BLOG,
- fileName: 'medium.png',
},
{
title: 'Twitter',
isExternal: true,
path: constants.URL_TWITTER,
- fileName: 'twitter.png',
},
{
title: 'Reddit',
isExternal: true,
path: constants.URL_REDDIT,
- fileName: 'reddit.png',
+ },
+ {
+ title: 'Forum',
+ isExternal: true,
+ path: constants.URL_DISCOURSE_FORUM,
},
],
Organization: [
@@ -105,6 +105,7 @@ const titleToIcon: { [title: string]: string } = {
Blog: 'medium.png',
Twitter: 'twitter.png',
Reddit: 'reddit.png',
+ Forum: 'discourse.png',
};
export interface FooterProps {}
diff --git a/packages/website/ts/components/generate_order/new_token_form.tsx b/packages/website/ts/components/generate_order/new_token_form.tsx
index b416de168..63645be9a 100644
--- a/packages/website/ts/components/generate_order/new_token_form.tsx
+++ b/packages/website/ts/components/generate_order/new_token_form.tsx
@@ -189,8 +189,8 @@ export class NewTokenForm extends React.Component<NewTokenFormProps, NewTokenFor
const tokenWithSymbolExists = !_.isUndefined(_.find(tokens, { symbol }));
if (symbol === '') {
symbolErrText = 'Symbol is required';
- } else if (!this._isLetters(symbol)) {
- symbolErrText = 'Can only include letters';
+ } else if (!this._isAlphanumeric(symbol)) {
+ symbolErrText = 'Can only include alphanumeric characters';
} else if (symbol.length > maxLength) {
symbolErrText = `Max length is ${maxLength}`;
} else if (tokenWithSymbolExists) {
@@ -231,7 +231,7 @@ export class NewTokenForm extends React.Component<NewTokenFormProps, NewTokenFor
private _isInteger(input: string) {
return /^[0-9]+$/i.test(input);
}
- private _isLetters(input: string) {
- return /^[a-zA-Z]+$/i.test(input);
+ private _isAlphanumeric(input: string) {
+ return /^[a-zA-Z0-9]+$/i.test(input);
}
}
diff --git a/packages/website/ts/components/token_balances.tsx b/packages/website/ts/components/token_balances.tsx
index 5e7b4e9f6..2cef413c7 100644
--- a/packages/website/ts/components/token_balances.tsx
+++ b/packages/website/ts/components/token_balances.tsx
@@ -169,7 +169,7 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala
? 'In order to try out the 0x Portal Dapp, request some test ether to pay for \
gas costs. It might take a bit of time for the test ether to show up.'
: 'Ether must be converted to Ether Tokens in order to be tradable via 0x. \
- You can convert between Ether and Ether Tokens by clicking the "convert" button below.'}
+ You can convert between Ether and Ether Tokens from the "Wrap ETH" tab.'}
</div>
<Table selectable={false} style={styles.bgColor}>
<TableHeader displaySelectAll={false} adjustForCheckbox={false}>
diff --git a/packages/website/ts/components/top_bar.tsx b/packages/website/ts/components/top_bar.tsx
index 601471222..11d3e7cc2 100644
--- a/packages/website/ts/components/top_bar.tsx
+++ b/packages/website/ts/components/top_bar.tsx
@@ -168,14 +168,14 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> {
</div>
)}
{this.props.blockchainIsLoaded &&
- !_.isEmpty(this.props.userAddress) && <div className="col col-5">{this._renderUser()}</div>}
- {!this._isViewingPortal() && (
- <div className={`col ${isFullWidthPage ? 'col-2 pl2' : 'col-1'} md-hide lg-hide`}>
- <div style={menuIconStyle}>
- <i className="zmdi zmdi-menu" onClick={this._onMenuButtonClick.bind(this)} />
- </div>
+ !_.isEmpty(this.props.userAddress) && (
+ <div className="col col-5 sm-hide xs-hide">{this._renderUser()}</div>
+ )}
+ <div className={`col ${isFullWidthPage ? 'col-2 pl2' : 'col-1'} md-hide lg-hide`}>
+ <div style={menuIconStyle}>
+ <i className="zmdi zmdi-menu" onClick={this._onMenuButtonClick.bind(this)} />
</div>
- )}
+ </div>
</div>
{this._renderDrawer()}
</div>
diff --git a/packages/website/ts/utils/configs.ts b/packages/website/ts/utils/configs.ts
index 52b400d57..3d37a89ab 100644
--- a/packages/website/ts/utils/configs.ts
+++ b/packages/website/ts/utils/configs.ts
@@ -118,8 +118,8 @@ export const configs = {
] as OutdatedWrappedEtherByNetworkId[],
// The order matters. We first try first node and only then fall back to others.
PUBLIC_NODE_URLS_BY_NETWORK_ID: {
- [1]: [`https://mainnet.infura.io/${INFURA_API_KEY}`],
- [42]: [`https://kovan.infura.io/${INFURA_API_KEY}`],
+ [1]: [`https://mainnet.infura.io/${INFURA_API_KEY}`, 'https://mainnet.0xproject.com'],
+ [42]: [`https://kovan.infura.io/${INFURA_API_KEY}`, 'https://kovan.0xproject.com'],
} as PublicNodeUrlsByNetworkId,
SHOULD_DEPRECATE_OLD_WETH_TOKEN: true,
SYMBOLS_OF_MINTABLE_TOKENS: ['MKR', 'MLN', 'GNT', 'DGD', 'REP'],
diff --git a/packages/website/ts/utils/constants.ts b/packages/website/ts/utils/constants.ts
index 3fc1e6397..dded82114 100644
--- a/packages/website/ts/utils/constants.ts
+++ b/packages/website/ts/utils/constants.ts
@@ -65,6 +65,7 @@ export const constants = {
URL_BIGNUMBERJS_GITHUB: 'http://mikemcl.github.io/bignumber.js',
URL_BITLY_API: 'https://api-ssl.bitly.com',
URL_BLOG: 'https://blog.0xproject.com/latest',
+ URL_DISCOURSE_FORUM: 'https://forum.0xproject.com',
URL_FIREFOX_U2F_ADDON: 'https://addons.mozilla.org/en-US/firefox/addon/u2f-support-add-on/',
URL_ETHER_FAUCET: 'https://faucet.0xproject.com',
URL_GITHUB_ORG: 'https://github.com/0xProject',
diff --git a/packages/website/ts/web3_wrapper.ts b/packages/website/ts/web3_wrapper.ts
index 6f260dc48..415df6e8b 100644
--- a/packages/website/ts/web3_wrapper.ts
+++ b/packages/website/ts/web3_wrapper.ts
@@ -1,6 +1,7 @@
import { BigNumber, intervalUtils, promisify } from '@0xproject/utils';
import * as _ from 'lodash';
import { Dispatcher } from 'ts/redux/dispatcher';
+import { utils } from 'ts/utils/utils';
import * as Web3 from 'web3';
export class Web3Wrapper {
@@ -101,41 +102,48 @@ export class Web3Wrapper {
let prevNodeVersion: string;
this._prevUserEtherBalanceInEth = new BigNumber(0);
this._dispatcher.updateNetworkId(this._prevNetworkId);
- this._watchNetworkAndBalanceIntervalId = intervalUtils.setAsyncExcludingInterval(async () => {
- // Check for network state changes
- const currentNetworkId = await this.getNetworkIdIfExists();
- if (currentNetworkId !== this._prevNetworkId) {
- this._prevNetworkId = currentNetworkId;
- this._dispatcher.updateNetworkId(currentNetworkId);
- }
-
- // Check for node version changes
- const currentNodeVersion = await this.getNodeVersionAsync();
- if (currentNodeVersion !== prevNodeVersion) {
- prevNodeVersion = currentNodeVersion;
- this._dispatcher.updateNodeVersion(currentNodeVersion);
- }
-
- if (this._shouldPollUserAddress) {
- const userAddressIfExists = await this.getFirstAccountIfExistsAsync();
- // Update makerAddress on network change
- if (this._prevUserAddress !== userAddressIfExists) {
- this._prevUserAddress = userAddressIfExists;
- this._dispatcher.updateUserAddress(userAddressIfExists);
+ this._watchNetworkAndBalanceIntervalId = intervalUtils.setAsyncExcludingInterval(
+ async () => {
+ // Check for network state changes
+ const currentNetworkId = await this.getNetworkIdIfExists();
+ if (currentNetworkId !== this._prevNetworkId) {
+ this._prevNetworkId = currentNetworkId;
+ this._dispatcher.updateNetworkId(currentNetworkId);
}
- // Check for user ether balance changes
- if (userAddressIfExists !== '') {
- await this._updateUserEtherBalanceAsync(userAddressIfExists);
+ // Check for node version changes
+ const currentNodeVersion = await this.getNodeVersionAsync();
+ if (currentNodeVersion !== prevNodeVersion) {
+ prevNodeVersion = currentNodeVersion;
+ this._dispatcher.updateNodeVersion(currentNodeVersion);
}
- } else {
- // This logic is primarily for the Ledger, since we don't regularly poll for the address
- // we simply update the balance for the last fetched address.
- if (!_.isEmpty(this._prevUserAddress)) {
- await this._updateUserEtherBalanceAsync(this._prevUserAddress);
+
+ if (this._shouldPollUserAddress) {
+ const userAddressIfExists = await this.getFirstAccountIfExistsAsync();
+ // Update makerAddress on network change
+ if (this._prevUserAddress !== userAddressIfExists) {
+ this._prevUserAddress = userAddressIfExists;
+ this._dispatcher.updateUserAddress(userAddressIfExists);
+ }
+
+ // Check for user ether balance changes
+ if (userAddressIfExists !== '') {
+ await this._updateUserEtherBalanceAsync(userAddressIfExists);
+ }
+ } else {
+ // This logic is primarily for the Ledger, since we don't regularly poll for the address
+ // we simply update the balance for the last fetched address.
+ if (!_.isEmpty(this._prevUserAddress)) {
+ await this._updateUserEtherBalanceAsync(this._prevUserAddress);
+ }
}
- }
- }, 5000);
+ },
+ 5000,
+ (err: Error) => {
+ utils.consoleLog(`Watching network and balances failed: ${err}`);
+ this._stopEmittingNetworkConnectionAndUserBalanceStateAsync();
+ },
+ );
}
private async _updateUserEtherBalanceAsync(userAddress: string) {
const balance = await this.getBalanceInEthAsync(userAddress);
@@ -145,6 +153,6 @@ export class Web3Wrapper {
}
}
private _stopEmittingNetworkConnectionAndUserBalanceStateAsync() {
- clearInterval(this._watchNetworkAndBalanceIntervalId);
+ intervalUtils.clearAsyncExcludingInterval(this._watchNetworkAndBalanceIntervalId);
}
}