diff options
author | Leonid <logvinov.leon@gmail.com> | 2018-01-19 20:39:57 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-01-19 20:39:57 +0800 |
commit | 6091f818da7cddcb4a22a573f7726ba9b76b398e (patch) | |
tree | 5769ca73d50c497e46e6c6c927700ae0fa00bec9 | |
parent | a45f9b480233d207fa5ba80a8e47cc3193c243d8 (diff) | |
parent | 9ccf63b44afe1ae6ccc2b9298ee35697d6be9e30 (diff) | |
download | dexon-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.md | 3 | ||||
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | packages/0x.js/CHANGELOG.md | 4 | ||||
-rw-r--r-- | packages/0x.js/test/exchange_wrapper_test.ts | 47 | ||||
-rw-r--r-- | packages/assert/src/index.ts | 2 | ||||
-rw-r--r-- | packages/assert/test/assert_test.ts | 14 | ||||
-rw-r--r-- | packages/website/public/images/social/discourse.png | bin | 0 -> 1454 bytes | |||
-rw-r--r-- | packages/website/ts/blockchain.ts | 24 | ||||
-rw-r--r-- | packages/website/ts/components/footer.tsx | 11 | ||||
-rw-r--r-- | packages/website/ts/components/generate_order/new_token_form.tsx | 8 | ||||
-rw-r--r-- | packages/website/ts/components/token_balances.tsx | 2 | ||||
-rw-r--r-- | packages/website/ts/components/top_bar.tsx | 14 | ||||
-rw-r--r-- | packages/website/ts/utils/configs.ts | 4 | ||||
-rw-r--r-- | packages/website/ts/utils/constants.ts | 1 | ||||
-rw-r--r-- | packages/website/ts/web3_wrapper.ts | 72 |
15 files changed, 147 insertions, 61 deletions
@@ -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 Binary files differnew file mode 100644 index 000000000..4bca3de0d --- /dev/null +++ b/packages/website/public/images/social/discourse.png 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); } } |