merge v2-prototype

This commit is contained in:
Fabio Berger 2018-07-05 00:38:15 +02:00
commit a0b9c7777b
113 changed files with 1415 additions and 815 deletions

View File

@ -6,8 +6,11 @@
This repository is a monorepo including the 0x protocol smart contracts and numerous developer tools. Each public sub-package is independently published to NPM. This repository is a monorepo including the 0x protocol smart contracts and numerous developer tools. Each public sub-package is independently published to NPM.
If you're developing on 0x now or are interested in using 0x infrastructure in the future, please join our [developer mailing list][dev-mailing-list-url] for updates.
[website-url]: https://0xproject.com/ [website-url]: https://0xproject.com/
[whitepaper-url]: https://0xproject.com/pdfs/0x_white_paper.pdf [whitepaper-url]: https://0xproject.com/pdfs/0x_white_paper.pdf
[dev-mailing-list-url]: http://eepurl.com/dx4cPf
[![CircleCI](https://circleci.com/gh/0xProject/0x-monorepo.svg?style=svg&circle-token=61bf7cd8c9b4e11b132089dfcffdd1be277d1e0c)](https://circleci.com/gh/0xProject/0x-monorepo) [![CircleCI](https://circleci.com/gh/0xProject/0x-monorepo.svg?style=svg&circle-token=61bf7cd8c9b4e11b132089dfcffdd1be277d1e0c)](https://circleci.com/gh/0xProject/0x-monorepo)
[![Coverage Status](https://coveralls.io/repos/github/0xProject/0x-monorepo/badge.svg?branch=development)](https://coveralls.io/github/0xProject/0x-monorepo?branch=development) [![Coverage Status](https://coveralls.io/repos/github/0xProject/0x-monorepo/badge.svg?branch=development)](https://coveralls.io/github/0xProject/0x-monorepo?branch=development)

View File

@ -110,7 +110,7 @@
"@0xproject/typescript-typings": "^0.4.1", "@0xproject/typescript-typings": "^0.4.1",
"@0xproject/utils": "^0.7.1", "@0xproject/utils": "^0.7.1",
"@0xproject/web3-wrapper": "^0.7.1", "@0xproject/web3-wrapper": "^0.7.1",
"ethereum-types": "^0.0.1", "ethereum-types": "^0.0.2",
"ethers": "3.0.22", "ethers": "3.0.22",
"lodash": "^4.17.4" "lodash": "^4.17.4"
}, },

View File

@ -29,7 +29,7 @@
"dependencies": { "dependencies": {
"@0xproject/typescript-typings": "^0.4.1", "@0xproject/typescript-typings": "^0.4.1",
"@0xproject/utils": "^0.7.1", "@0xproject/utils": "^0.7.1",
"ethereum-types": "^0.0.1", "ethereum-types": "^0.0.2",
"chalk": "^2.3.0", "chalk": "^2.3.0",
"glob": "^7.1.2", "glob": "^7.1.2",
"handlebars": "^4.0.11", "handlebars": "^4.0.11",

View File

@ -43,7 +43,7 @@
"typescript": "2.7.1" "typescript": "2.7.1"
}, },
"dependencies": { "dependencies": {
"ethereum-types": "^0.0.1", "ethereum-types": "^0.0.2",
"@0xproject/typescript-typings": "^0.4.1", "@0xproject/typescript-typings": "^0.4.1",
"@0xproject/utils": "^0.7.1", "@0xproject/utils": "^0.7.1",
"@0xproject/web3-wrapper": "^0.7.1", "@0xproject/web3-wrapper": "^0.7.1",

View File

@ -1,4 +1,12 @@
[ [
{
"version": "0.0.6",
"changes": [
{
"note": "Update blockstream to v5.0 and propogate up caught errors to active subscriptions"
}
]
},
{ {
"timestamp": 1529397769, "timestamp": 1529397769,
"version": "0.0.5", "version": "0.0.5",

View File

@ -74,7 +74,7 @@
"source-map-support": "^0.5.0", "source-map-support": "^0.5.0",
"tslint": "5.8.0", "tslint": "5.8.0",
"typescript": "2.7.1", "typescript": "2.7.1",
"web3-provider-engine": "^14.0.4" "web3-provider-engine": "14.0.6"
}, },
"dependencies": { "dependencies": {
"@0xproject/assert": "^0.2.12", "@0xproject/assert": "^0.2.12",
@ -86,8 +86,8 @@
"@0xproject/typescript-typings": "^0.4.1", "@0xproject/typescript-typings": "^0.4.1",
"@0xproject/utils": "^0.7.1", "@0xproject/utils": "^0.7.1",
"@0xproject/web3-wrapper": "^0.7.1", "@0xproject/web3-wrapper": "^0.7.1",
"ethereum-types": "^0.0.1", "ethereum-types": "^0.0.2",
"ethereumjs-blockstream": "^2.0.6", "ethereumjs-blockstream": "5.0.0",
"ethereumjs-util": "^5.1.1", "ethereumjs-util": "^5.1.1",
"ethers": "3.0.22", "ethers": "3.0.22",
"js-sha3": "^0.7.0", "js-sha3": "^0.7.0",

View File

@ -9,7 +9,7 @@ import {
} from '@0xproject/types'; } from '@0xproject/types';
import { intervalUtils } from '@0xproject/utils'; import { intervalUtils } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper'; import { Web3Wrapper } from '@0xproject/web3-wrapper';
import { Block, BlockAndLogStreamer } from 'ethereumjs-blockstream'; import { Block, BlockAndLogStreamer, Log } from 'ethereumjs-blockstream';
import * as _ from 'lodash'; import * as _ from 'lodash';
import { import {
@ -39,7 +39,7 @@ export abstract class ContractWrapper {
public abstract abi: ContractAbi; public abstract abi: ContractAbi;
protected _web3Wrapper: Web3Wrapper; protected _web3Wrapper: Web3Wrapper;
protected _networkId: number; protected _networkId: number;
private _blockAndLogStreamerIfExists?: BlockAndLogStreamer; private _blockAndLogStreamerIfExists: BlockAndLogStreamer<Block, Log> | undefined;
private _blockAndLogStreamIntervalIfExists?: NodeJS.Timer; private _blockAndLogStreamIntervalIfExists?: NodeJS.Timer;
private _filters: { [filterToken: string]: FilterObject }; private _filters: { [filterToken: string]: FilterObject };
private _filterCallbacks: { private _filterCallbacks: {
@ -163,6 +163,7 @@ export abstract class ContractWrapper {
this._blockAndLogStreamerIfExists = new BlockAndLogStreamer( this._blockAndLogStreamerIfExists = new BlockAndLogStreamer(
this._web3Wrapper.getBlockAsync.bind(this._web3Wrapper), this._web3Wrapper.getBlockAsync.bind(this._web3Wrapper),
this._web3Wrapper.getLogsAsync.bind(this._web3Wrapper), this._web3Wrapper.getLogsAsync.bind(this._web3Wrapper),
this._onBlockAndLogStreamerError.bind(this),
); );
const catchAllLogFilter = {}; const catchAllLogFilter = {};
this._blockAndLogStreamerIfExists.addLogFilter(catchAllLogFilter); this._blockAndLogStreamerIfExists.addLogFilter(catchAllLogFilter);
@ -180,6 +181,14 @@ export abstract class ContractWrapper {
this._onLogStateChanged.bind(this, isRemoved), this._onLogStateChanged.bind(this, isRemoved),
); );
} }
private _onBlockAndLogStreamerError(err: Error): void {
// Propogate all Blockstream subscriber errors to all
// top-level subscriptions
const filterCallbacks = _.values(this._filterCallbacks);
_.each(filterCallbacks, filterCallback => {
filterCallback(err);
});
}
private _onReconcileBlockError(err: Error): void { private _onReconcileBlockError(err: Error): void {
const filterTokens = _.keys(this._filterCallbacks); const filterTokens = _.keys(this._filterCallbacks);
_.each(filterTokens, filterToken => { _.each(filterTokens, filterToken => {

View File

@ -30,8 +30,8 @@ const schemaErrorTransformer = (error: Error) => {
*/ */
const asyncErrorHandlerFactory = (errorTransformer: ErrorTransformer) => { const asyncErrorHandlerFactory = (errorTransformer: ErrorTransformer) => {
const asyncErrorHandlingDecorator = ( const asyncErrorHandlingDecorator = (
target: object, _target: object,
key: string | symbol, _key: string | symbol,
descriptor: TypedPropertyDescriptor<AsyncMethod>, descriptor: TypedPropertyDescriptor<AsyncMethod>,
) => { ) => {
const originalMethod = descriptor.value as AsyncMethod; const originalMethod = descriptor.value as AsyncMethod;
@ -57,8 +57,8 @@ const asyncErrorHandlerFactory = (errorTransformer: ErrorTransformer) => {
const syncErrorHandlerFactory = (errorTransformer: ErrorTransformer) => { const syncErrorHandlerFactory = (errorTransformer: ErrorTransformer) => {
const syncErrorHandlingDecorator = ( const syncErrorHandlingDecorator = (
target: object, _target: object,
key: string | symbol, _key: string | symbol,
descriptor: TypedPropertyDescriptor<SyncMethod>, descriptor: TypedPropertyDescriptor<SyncMethod>,
) => { ) => {
const originalMethod = descriptor.value as SyncMethod; const originalMethod = descriptor.value as SyncMethod;

View File

@ -1,8 +1,7 @@
import { BlockParamLiteral, ExchangeContractErrs } from '@0xproject/types'; import { ExchangeContractErrs } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils'; import { BigNumber } from '@0xproject/utils';
import { AbstractBalanceAndProxyAllowanceLazyStore } from '../abstract/abstract_balance_and_proxy_allowance_lazy_store'; import { AbstractBalanceAndProxyAllowanceLazyStore } from '../abstract/abstract_balance_and_proxy_allowance_lazy_store';
import { TokenWrapper } from '../contract_wrappers/token_wrapper';
import { TradeSide, TransferType } from '../types'; import { TradeSide, TransferType } from '../types';
import { constants } from '../utils/constants'; import { constants } from '../utils/constants';

View File

@ -30,7 +30,7 @@ export const filterUtils = {
blockRange?: BlockRange, blockRange?: BlockRange,
): FilterObject { ): FilterObject {
const eventAbi = _.find(abi, { name: eventName }) as EventAbi; const eventAbi = _.find(abi, { name: eventName }) as EventAbi;
const eventSignature = filterUtils.getEventSignatureFromAbiByName(eventAbi, eventName); const eventSignature = filterUtils.getEventSignatureFromAbiByName(eventAbi);
const topicForEventSignature = ethUtil.addHexPrefix(jsSHA3.keccak256(eventSignature)); const topicForEventSignature = ethUtil.addHexPrefix(jsSHA3.keccak256(eventSignature));
const topicsForIndexedArgs = filterUtils.getTopicsForIndexedArgs(eventAbi, indexFilterValues); const topicsForIndexedArgs = filterUtils.getTopicsForIndexedArgs(eventAbi, indexFilterValues);
const topics = [topicForEventSignature, ...topicsForIndexedArgs]; const topics = [topicForEventSignature, ...topicsForIndexedArgs];
@ -46,7 +46,7 @@ export const filterUtils = {
} }
return filter; return filter;
}, },
getEventSignatureFromAbiByName(eventAbi: EventAbi, eventName: ContractEvents): string { getEventSignatureFromAbiByName(eventAbi: EventAbi): string {
const types = _.map(eventAbi.inputs, 'type'); const types = _.map(eventAbi.inputs, 'type');
const signature = `${eventAbi.name}(${types.join(',')})`; const signature = `${eventAbi.name}(${types.join(',')})`;
return signature; return signature;

View File

@ -277,7 +277,7 @@ describe('EtherTokenWrapper', () => {
it('should cancel outstanding subscriptions when ZeroEx.setProvider is called', (done: DoneCallback) => { it('should cancel outstanding subscriptions when ZeroEx.setProvider is called', (done: DoneCallback) => {
(async () => { (async () => {
const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)( const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)(
(logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => { (_logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
done(new Error('Expected this subscription to have been cancelled')); done(new Error('Expected this subscription to have been cancelled'));
}, },
); );
@ -307,7 +307,7 @@ describe('EtherTokenWrapper', () => {
it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => { it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => {
(async () => { (async () => {
const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)( const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)(
(logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => { (_logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
done(new Error('Expected this subscription to have been cancelled')); done(new Error('Expected this subscription to have been cancelled'));
}, },
); );

View File

@ -999,7 +999,7 @@ describe('ExchangeWrapper', () => {
it('Outstanding subscriptions are cancelled when contractWrappers.setProvider called', (done: DoneCallback) => { it('Outstanding subscriptions are cancelled when contractWrappers.setProvider called', (done: DoneCallback) => {
(async () => { (async () => {
const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)( const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)(
(logEvent: DecodedLogEvent<LogFillContractEventArgs>) => { (_logEvent: DecodedLogEvent<LogFillContractEventArgs>) => {
done(new Error('Expected this subscription to have been cancelled')); done(new Error('Expected this subscription to have been cancelled'));
}, },
); );
@ -1024,7 +1024,7 @@ describe('ExchangeWrapper', () => {
it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => { it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => {
(async () => { (async () => {
const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)( const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)(
(logEvent: DecodedLogEvent<LogFillContractEventArgs>) => { (_logEvent: DecodedLogEvent<LogFillContractEventArgs>) => {
done(new Error('Expected this subscription to have been cancelled')); done(new Error('Expected this subscription to have been cancelled'));
}, },
); );

View File

@ -80,7 +80,7 @@ describe('SubscriptionTest', () => {
}); });
it('Should allow unsubscribeAll to be called successfully after an error', (done: DoneCallback) => { it('Should allow unsubscribeAll to be called successfully after an error', (done: DoneCallback) => {
(async () => { (async () => {
const callback = (err: Error | null, logEvent?: DecodedLogEvent<ApprovalContractEventArgs>) => _.noop; const callback = (_err: Error | null, _logEvent?: DecodedLogEvent<ApprovalContractEventArgs>) => _.noop;
contractWrappers.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback); contractWrappers.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback);
stubs = [ stubs = [
Sinon.stub((contractWrappers as any)._web3Wrapper, 'getBlockAsync').throws( Sinon.stub((contractWrappers as any)._web3Wrapper, 'getBlockAsync').throws(

View File

@ -484,7 +484,7 @@ describe('TokenWrapper', () => {
it('Outstanding subscriptions are cancelled when contractWrappers.setProvider called', (done: DoneCallback) => { it('Outstanding subscriptions are cancelled when contractWrappers.setProvider called', (done: DoneCallback) => {
(async () => { (async () => {
const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)( const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)(
(logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => { (_logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
done(new Error('Expected this subscription to have been cancelled')); done(new Error('Expected this subscription to have been cancelled'));
}, },
); );
@ -508,7 +508,7 @@ describe('TokenWrapper', () => {
it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => { it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => {
(async () => { (async () => {
const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)( const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)(
(logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => { (_logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
done(new Error('Expected this subscription to have been cancelled')); done(new Error('Expected this subscription to have been cancelled'));
}, },
); );

View File

@ -80,12 +80,11 @@
"@0xproject/typescript-typings": "^0.4.1", "@0xproject/typescript-typings": "^0.4.1",
"@0xproject/utils": "^0.7.1", "@0xproject/utils": "^0.7.1",
"@0xproject/web3-wrapper": "^0.7.1", "@0xproject/web3-wrapper": "^0.7.1",
"ethereum-types": "^0.0.1", "ethereum-types": "^0.0.2",
"bn.js": "^4.11.8", "bn.js": "^4.11.8",
"ethereumjs-abi": "^0.6.4", "ethereumjs-abi": "^0.6.4",
"ethereumjs-util": "^5.1.1", "ethereumjs-util": "^5.1.1",
"ethers": "3.0.22", "ethers": "3.0.22",
"lodash": "^4.17.4", "lodash": "^4.17.4"
"web3": "^0.20.0"
} }
} }

View File

@ -5,7 +5,7 @@ import * as chai from 'chai';
import { MixinAuthorizableContract } from '../../generated_contract_wrappers/mixin_authorizable'; import { MixinAuthorizableContract } from '../../generated_contract_wrappers/mixin_authorizable';
import { artifacts } from '../utils/artifacts'; import { artifacts } from '../utils/artifacts';
import { expectRevertReasonOrAlwaysFailingTransactionAsync } from '../utils/assertions'; import { expectTransactionFailedAsync } from '../utils/assertions';
import { chaiSetup } from '../utils/chai_setup'; import { chaiSetup } from '../utils/chai_setup';
import { constants } from '../utils/constants'; import { constants } from '../utils/constants';
import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper'; import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper';
@ -44,7 +44,7 @@ describe('Authorizable', () => {
}); });
describe('addAuthorizedAddress', () => { describe('addAuthorizedAddress', () => {
it('should throw if not called by owner', async () => { it('should throw if not called by owner', async () => {
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
authorizable.addAuthorizedAddress.sendTransactionAsync(notOwner, { from: notOwner }), authorizable.addAuthorizedAddress.sendTransactionAsync(notOwner, { from: notOwner }),
RevertReason.OnlyContractOwner, RevertReason.OnlyContractOwner,
); );
@ -62,7 +62,7 @@ describe('Authorizable', () => {
await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }), await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
constants.AWAIT_TRANSACTION_MINED_MS, constants.AWAIT_TRANSACTION_MINED_MS,
); );
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }), authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
RevertReason.TargetAlreadyAuthorized, RevertReason.TargetAlreadyAuthorized,
); );
@ -75,7 +75,7 @@ describe('Authorizable', () => {
await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }), await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
constants.AWAIT_TRANSACTION_MINED_MS, constants.AWAIT_TRANSACTION_MINED_MS,
); );
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
authorizable.removeAuthorizedAddress.sendTransactionAsync(address, { authorizable.removeAuthorizedAddress.sendTransactionAsync(address, {
from: notOwner, from: notOwner,
}), }),
@ -99,7 +99,7 @@ describe('Authorizable', () => {
}); });
it('should throw if owner attempts to remove an address that is not authorized', async () => { it('should throw if owner attempts to remove an address that is not authorized', async () => {
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
authorizable.removeAuthorizedAddress.sendTransactionAsync(address, { authorizable.removeAuthorizedAddress.sendTransactionAsync(address, {
from: owner, from: owner,
}), }),
@ -115,7 +115,7 @@ describe('Authorizable', () => {
constants.AWAIT_TRANSACTION_MINED_MS, constants.AWAIT_TRANSACTION_MINED_MS,
); );
const index = new BigNumber(0); const index = new BigNumber(0);
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, { authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, {
from: notOwner, from: notOwner,
}), }),
@ -128,7 +128,7 @@ describe('Authorizable', () => {
constants.AWAIT_TRANSACTION_MINED_MS, constants.AWAIT_TRANSACTION_MINED_MS,
); );
const index = new BigNumber(1); const index = new BigNumber(1);
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, { authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, {
from: owner, from: owner,
}), }),
@ -137,7 +137,7 @@ describe('Authorizable', () => {
}); });
it('should throw if owner attempts to remove an address that is not authorized', async () => { it('should throw if owner attempts to remove an address that is not authorized', async () => {
const index = new BigNumber(0); const index = new BigNumber(0);
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, { authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, {
from: owner, from: owner,
}), }),
@ -156,7 +156,7 @@ describe('Authorizable', () => {
constants.AWAIT_TRANSACTION_MINED_MS, constants.AWAIT_TRANSACTION_MINED_MS,
); );
const address1Index = new BigNumber(0); const address1Index = new BigNumber(0);
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address2, address1Index, { authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address2, address1Index, {
from: owner, from: owner,
}), }),

View File

@ -17,7 +17,7 @@ import { ERC20ProxyContract } from '../../generated_contract_wrappers/e_r_c20_pr
import { ERC721ProxyContract } from '../../generated_contract_wrappers/e_r_c721_proxy'; import { ERC721ProxyContract } from '../../generated_contract_wrappers/e_r_c721_proxy';
import { IAssetProxyContract } from '../../generated_contract_wrappers/i_asset_proxy'; import { IAssetProxyContract } from '../../generated_contract_wrappers/i_asset_proxy';
import { artifacts } from '../utils/artifacts'; import { artifacts } from '../utils/artifacts';
import { expectRevertReasonOrAlwaysFailingTransactionAsync } from '../utils/assertions'; import { expectTransactionFailedAsync } from '../utils/assertions';
import { chaiSetup } from '../utils/chai_setup'; import { chaiSetup } from '../utils/chai_setup';
import { constants } from '../utils/constants'; import { constants } from '../utils/constants';
import { ERC20Wrapper } from '../utils/erc20_wrapper'; import { ERC20Wrapper } from '../utils/erc20_wrapper';
@ -184,7 +184,7 @@ describe('Asset Transfer Proxies', () => {
constants.AWAIT_TRANSACTION_MINED_MS, constants.AWAIT_TRANSACTION_MINED_MS,
); );
// Perform a transfer; expect this to fail. // Perform a transfer; expect this to fail.
return expectRevertReasonOrAlwaysFailingTransactionAsync( await expectTransactionFailedAsync(
web3Wrapper.sendTransactionAsync({ web3Wrapper.sendTransactionAsync({
to: erc20Proxy.address, to: erc20Proxy.address,
data, data,
@ -205,7 +205,7 @@ describe('Asset Transfer Proxies', () => {
takerAddress, takerAddress,
amount, amount,
); );
return expectRevertReasonOrAlwaysFailingTransactionAsync( await expectTransactionFailedAsync(
web3Wrapper.sendTransactionAsync({ web3Wrapper.sendTransactionAsync({
to: erc20Proxy.address, to: erc20Proxy.address,
data, data,
@ -344,7 +344,7 @@ describe('Asset Transfer Proxies', () => {
erc20Proxy.address, // the ERC20 proxy does not have an ERC721 receiver erc20Proxy.address, // the ERC20 proxy does not have an ERC721 receiver
amount, amount,
); );
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
web3Wrapper.sendTransactionAsync({ web3Wrapper.sendTransactionAsync({
to: erc721Proxy.address, to: erc721Proxy.address,
data, data,
@ -369,7 +369,7 @@ describe('Asset Transfer Proxies', () => {
takerAddress, takerAddress,
amount, amount,
); );
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
web3Wrapper.sendTransactionAsync({ web3Wrapper.sendTransactionAsync({
to: erc721Proxy.address, to: erc721Proxy.address,
data, data,
@ -393,7 +393,7 @@ describe('Asset Transfer Proxies', () => {
takerAddress, takerAddress,
amount, amount,
); );
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
web3Wrapper.sendTransactionAsync({ web3Wrapper.sendTransactionAsync({
to: erc721Proxy.address, to: erc721Proxy.address,
data, data,
@ -421,7 +421,7 @@ describe('Asset Transfer Proxies', () => {
takerAddress, takerAddress,
amount, amount,
); );
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
web3Wrapper.sendTransactionAsync({ web3Wrapper.sendTransactionAsync({
to: erc721Proxy.address, to: erc721Proxy.address,
data, data,
@ -442,7 +442,7 @@ describe('Asset Transfer Proxies', () => {
takerAddress, takerAddress,
amount, amount,
); );
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
web3Wrapper.sendTransactionAsync({ web3Wrapper.sendTransactionAsync({
to: erc721Proxy.address, to: erc721Proxy.address,
data, data,

View File

@ -14,7 +14,7 @@ import { ERC20ProxyContract } from '../../generated_contract_wrappers/e_r_c20_pr
import { ERC721ProxyContract } from '../../generated_contract_wrappers/e_r_c721_proxy'; import { ERC721ProxyContract } from '../../generated_contract_wrappers/e_r_c721_proxy';
import { CancelContractEventArgs, ExchangeContract } from '../../generated_contract_wrappers/exchange'; import { CancelContractEventArgs, ExchangeContract } from '../../generated_contract_wrappers/exchange';
import { artifacts } from '../utils/artifacts'; import { artifacts } from '../utils/artifacts';
import { expectRevertReasonOrAlwaysFailingTransactionAsync } from '../utils/assertions'; import { expectTransactionFailedAsync } from '../utils/assertions';
import { chaiSetup } from '../utils/chai_setup'; import { chaiSetup } from '../utils/chai_setup';
import { constants } from '../utils/constants'; import { constants } from '../utils/constants';
import { ERC20Wrapper } from '../utils/erc20_wrapper'; import { ERC20Wrapper } from '../utils/erc20_wrapper';
@ -144,7 +144,7 @@ describe('Exchange core', () => {
const invalidSigBuff = Buffer.concat([v, invalidR, invalidS, signatureType]); const invalidSigBuff = Buffer.concat([v, invalidR, invalidS, signatureType]);
const invalidSigHex = `0x${invalidSigBuff.toString('hex')}`; const invalidSigHex = `0x${invalidSigBuff.toString('hex')}`;
signedOrder.signature = invalidSigHex; signedOrder.signature = invalidSigHex;
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), exchangeWrapper.fillOrderAsync(signedOrder, takerAddress),
RevertReason.InvalidOrderSignature, RevertReason.InvalidOrderSignature,
); );
@ -153,7 +153,7 @@ describe('Exchange core', () => {
it('should throw if no value is filled', async () => { it('should throw if no value is filled', async () => {
signedOrder = orderFactory.newSignedOrder(); signedOrder = orderFactory.newSignedOrder();
await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress); await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress);
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), exchangeWrapper.fillOrderAsync(signedOrder, takerAddress),
RevertReason.OrderUnfillable, RevertReason.OrderUnfillable,
); );
@ -167,7 +167,7 @@ describe('Exchange core', () => {
}); });
it('should throw if not sent by maker', async () => { it('should throw if not sent by maker', async () => {
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.cancelOrderAsync(signedOrder, takerAddress), exchangeWrapper.cancelOrderAsync(signedOrder, takerAddress),
RevertReason.InvalidMaker, RevertReason.InvalidMaker,
); );
@ -178,7 +178,7 @@ describe('Exchange core', () => {
makerAssetAmount: new BigNumber(0), makerAssetAmount: new BigNumber(0),
}); });
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress), exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress),
RevertReason.OrderUnfillable, RevertReason.OrderUnfillable,
); );
@ -189,7 +189,7 @@ describe('Exchange core', () => {
takerAssetAmount: new BigNumber(0), takerAssetAmount: new BigNumber(0),
}); });
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress), exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress),
RevertReason.OrderUnfillable, RevertReason.OrderUnfillable,
); );
@ -197,7 +197,7 @@ describe('Exchange core', () => {
it('should be able to cancel a full order', async () => { it('should be able to cancel a full order', async () => {
await exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress); await exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress);
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, {
takerAssetFillAmount: signedOrder.takerAssetAmount.div(2), takerAssetFillAmount: signedOrder.takerAssetAmount.div(2),
}), }),
@ -222,7 +222,7 @@ describe('Exchange core', () => {
it('should throw if already cancelled', async () => { it('should throw if already cancelled', async () => {
await exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress); await exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress);
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress), exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress),
RevertReason.OrderUnfillable, RevertReason.OrderUnfillable,
); );
@ -232,7 +232,7 @@ describe('Exchange core', () => {
signedOrder = orderFactory.newSignedOrder({ signedOrder = orderFactory.newSignedOrder({
expirationTimeSeconds: new BigNumber(Math.floor((Date.now() - 10000) / 1000)), expirationTimeSeconds: new BigNumber(Math.floor((Date.now() - 10000) / 1000)),
}); });
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress), exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress),
RevertReason.OrderUnfillable, RevertReason.OrderUnfillable,
); );
@ -250,7 +250,7 @@ describe('Exchange core', () => {
}); });
const fillTakerAssetAmount2 = new BigNumber(1); const fillTakerAssetAmount2 = new BigNumber(1);
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, {
takerAssetFillAmount: fillTakerAssetAmount2, takerAssetFillAmount: fillTakerAssetAmount2,
}), }),
@ -264,7 +264,7 @@ describe('Exchange core', () => {
const orderEpoch = new BigNumber(1); const orderEpoch = new BigNumber(1);
await exchangeWrapper.cancelOrdersUpToAsync(orderEpoch, makerAddress); await exchangeWrapper.cancelOrdersUpToAsync(orderEpoch, makerAddress);
const lesserOrderEpoch = new BigNumber(0); const lesserOrderEpoch = new BigNumber(0);
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.cancelOrdersUpToAsync(lesserOrderEpoch, makerAddress), exchangeWrapper.cancelOrdersUpToAsync(lesserOrderEpoch, makerAddress),
RevertReason.InvalidNewOrderEpoch, RevertReason.InvalidNewOrderEpoch,
); );
@ -273,7 +273,7 @@ describe('Exchange core', () => {
it('should fail to set orderEpoch equal to existing orderEpoch', async () => { it('should fail to set orderEpoch equal to existing orderEpoch', async () => {
const orderEpoch = new BigNumber(1); const orderEpoch = new BigNumber(1);
await exchangeWrapper.cancelOrdersUpToAsync(orderEpoch, makerAddress); await exchangeWrapper.cancelOrdersUpToAsync(orderEpoch, makerAddress);
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.cancelOrdersUpToAsync(orderEpoch, makerAddress), exchangeWrapper.cancelOrdersUpToAsync(orderEpoch, makerAddress),
RevertReason.InvalidNewOrderEpoch, RevertReason.InvalidNewOrderEpoch,
); );
@ -363,7 +363,7 @@ describe('Exchange core', () => {
expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress); expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress);
// Call Exchange // Call Exchange
const takerAssetFillAmount = signedOrder.takerAssetAmount; const takerAssetFillAmount = signedOrder.takerAssetAmount;
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }), exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }),
RevertReason.TransferFailed, RevertReason.TransferFailed,
); );
@ -386,7 +386,7 @@ describe('Exchange core', () => {
expect(initialOwnerTakerAsset).to.be.bignumber.not.equal(takerAddress); expect(initialOwnerTakerAsset).to.be.bignumber.not.equal(takerAddress);
// Call Exchange // Call Exchange
const takerAssetFillAmount = signedOrder.takerAssetAmount; const takerAssetFillAmount = signedOrder.takerAssetAmount;
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }), exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }),
RevertReason.TransferFailed, RevertReason.TransferFailed,
); );
@ -409,7 +409,7 @@ describe('Exchange core', () => {
expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress); expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress);
// Call Exchange // Call Exchange
const takerAssetFillAmount = signedOrder.takerAssetAmount; const takerAssetFillAmount = signedOrder.takerAssetAmount;
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }), exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }),
RevertReason.InvalidAmount, RevertReason.InvalidAmount,
); );
@ -432,7 +432,7 @@ describe('Exchange core', () => {
expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress); expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress);
// Call Exchange // Call Exchange
const takerAssetFillAmount = signedOrder.takerAssetAmount; const takerAssetFillAmount = signedOrder.takerAssetAmount;
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }), exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }),
RevertReason.InvalidAmount, RevertReason.InvalidAmount,
); );
@ -449,7 +449,7 @@ describe('Exchange core', () => {
}); });
// Call Exchange // Call Exchange
const takerAssetFillAmount = signedOrder.takerAssetAmount.div(2); const takerAssetFillAmount = signedOrder.takerAssetAmount.div(2);
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }), exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }),
RevertReason.RoundingError, RevertReason.RoundingError,
); );
@ -475,7 +475,7 @@ describe('Exchange core', () => {
expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress); expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress);
// Call Exchange // Call Exchange
const takerAssetFillAmount = signedOrder.takerAssetAmount; const takerAssetFillAmount = signedOrder.takerAssetAmount;
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }), exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }),
RevertReason.LengthGreaterThan131Required, RevertReason.LengthGreaterThan131Required,
); );

View File

@ -14,7 +14,7 @@ import {
TestAssetProxyDispatcherContract, TestAssetProxyDispatcherContract,
} from '../../generated_contract_wrappers/test_asset_proxy_dispatcher'; } from '../../generated_contract_wrappers/test_asset_proxy_dispatcher';
import { artifacts } from '../utils/artifacts'; import { artifacts } from '../utils/artifacts';
import { expectRevertReasonOrAlwaysFailingTransactionAsync } from '../utils/assertions'; import { expectTransactionFailedAsync } from '../utils/assertions';
import { chaiSetup } from '../utils/chai_setup'; import { chaiSetup } from '../utils/chai_setup';
import { constants } from '../utils/constants'; import { constants } from '../utils/constants';
import { ERC20Wrapper } from '../utils/erc20_wrapper'; import { ERC20Wrapper } from '../utils/erc20_wrapper';
@ -129,7 +129,7 @@ describe('AssetProxyDispatcher', () => {
txDefaults, txDefaults,
); );
// Register new ERC20 Transfer Proxy contract // Register new ERC20 Transfer Proxy contract
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(newErc20TransferProxy.address, { assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(newErc20TransferProxy.address, {
from: owner, from: owner,
}), }),
@ -138,7 +138,7 @@ describe('AssetProxyDispatcher', () => {
}); });
it('should throw if requesting address is not owner', async () => { it('should throw if requesting address is not owner', async () => {
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, { from: notOwner }), assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, { from: notOwner }),
RevertReason.OnlyContractOwner, RevertReason.OnlyContractOwner,
); );
@ -210,7 +210,7 @@ describe('AssetProxyDispatcher', () => {
const encodedAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address); const encodedAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address);
// Perform a transfer from makerAddress to takerAddress // Perform a transfer from makerAddress to takerAddress
const amount = new BigNumber(10); const amount = new BigNumber(10);
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
assetProxyDispatcher.publicDispatchTransferFrom.sendTransactionAsync( assetProxyDispatcher.publicDispatchTransferFrom.sendTransactionAsync(
encodedAssetData, encodedAssetData,
makerAddress, makerAddress,

View File

@ -12,7 +12,7 @@ import { ERC20ProxyContract } from '../../generated_contract_wrappers/e_r_c20_pr
import { ERC721ProxyContract } from '../../generated_contract_wrappers/e_r_c721_proxy'; import { ERC721ProxyContract } from '../../generated_contract_wrappers/e_r_c721_proxy';
import { ExchangeContract } from '../../generated_contract_wrappers/exchange'; import { ExchangeContract } from '../../generated_contract_wrappers/exchange';
import { artifacts } from '../utils/artifacts'; import { artifacts } from '../utils/artifacts';
import { expectRevertReasonOrAlwaysFailingTransactionAsync } from '../utils/assertions'; import { expectTransactionFailedAsync } from '../utils/assertions';
import { chaiSetup } from '../utils/chai_setup'; import { chaiSetup } from '../utils/chai_setup';
import { constants } from '../utils/constants'; import { constants } from '../utils/constants';
import { ERC20Wrapper } from '../utils/erc20_wrapper'; import { ERC20Wrapper } from '../utils/erc20_wrapper';
@ -602,7 +602,7 @@ describe('matchOrders', () => {
// Cancel left order // Cancel left order
await exchangeWrapper.cancelOrderAsync(signedOrderLeft, signedOrderLeft.makerAddress); await exchangeWrapper.cancelOrderAsync(signedOrderLeft, signedOrderLeft.makerAddress);
// Match orders // Match orders
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress), exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress),
RevertReason.OrderUnfillable, RevertReason.OrderUnfillable,
); );
@ -627,7 +627,7 @@ describe('matchOrders', () => {
// Cancel right order // Cancel right order
await exchangeWrapper.cancelOrderAsync(signedOrderRight, signedOrderRight.makerAddress); await exchangeWrapper.cancelOrderAsync(signedOrderRight, signedOrderRight.makerAddress);
// Match orders // Match orders
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress), exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress),
RevertReason.OrderUnfillable, RevertReason.OrderUnfillable,
); );
@ -650,7 +650,7 @@ describe('matchOrders', () => {
feeRecipientAddress: feeRecipientAddressRight, feeRecipientAddress: feeRecipientAddressRight,
}); });
// Match orders // Match orders
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress), exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress),
RevertReason.NegativeSpreadRequired, RevertReason.NegativeSpreadRequired,
); );
@ -673,7 +673,7 @@ describe('matchOrders', () => {
feeRecipientAddress: feeRecipientAddressRight, feeRecipientAddress: feeRecipientAddressRight,
}); });
// Match orders // Match orders
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress), exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress),
// We are assuming assetData fields of the right order are the // We are assuming assetData fields of the right order are the
// reverse of the left order, rather than checking equality. This // reverse of the left order, rather than checking equality. This
@ -702,7 +702,7 @@ describe('matchOrders', () => {
feeRecipientAddress: feeRecipientAddressRight, feeRecipientAddress: feeRecipientAddressRight,
}); });
// Match orders // Match orders
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress), exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress),
RevertReason.InvalidOrderSignature, RevertReason.InvalidOrderSignature,
); );

View File

@ -13,7 +13,7 @@ import { TestValidatorContract } from '../../generated_contract_wrappers/test_va
import { TestWalletContract } from '../../generated_contract_wrappers/test_wallet'; import { TestWalletContract } from '../../generated_contract_wrappers/test_wallet';
import { addressUtils } from '../utils/address_utils'; import { addressUtils } from '../utils/address_utils';
import { artifacts } from '../utils/artifacts'; import { artifacts } from '../utils/artifacts';
import { expectRevertOrOtherErrorAsync } from '../utils/assertions'; import { expectContractCallFailed } from '../utils/assertions';
import { chaiSetup } from '../utils/chai_setup'; import { chaiSetup } from '../utils/chai_setup';
import { constants } from '../utils/constants'; import { constants } from '../utils/constants';
import { LogDecoder } from '../utils/log_decoder'; import { LogDecoder } from '../utils/log_decoder';
@ -101,7 +101,7 @@ describe('MixinSignatureValidator', () => {
it('should revert when signature is empty', async () => { it('should revert when signature is empty', async () => {
const emptySignature = '0x'; const emptySignature = '0x';
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
return expectRevertOrOtherErrorAsync( return expectContractCallFailed(
signatureValidator.publicIsValidSignature.callAsync( signatureValidator.publicIsValidSignature.callAsync(
orderHashHex, orderHashHex,
signedOrder.makerAddress, signedOrder.makerAddress,
@ -115,7 +115,7 @@ describe('MixinSignatureValidator', () => {
const unsupportedSignatureType = SignatureType.NSignatureTypes; const unsupportedSignatureType = SignatureType.NSignatureTypes;
const unsupportedSignatureHex = `0x${unsupportedSignatureType}`; const unsupportedSignatureHex = `0x${unsupportedSignatureType}`;
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
return expectRevertOrOtherErrorAsync( return expectContractCallFailed(
signatureValidator.publicIsValidSignature.callAsync( signatureValidator.publicIsValidSignature.callAsync(
orderHashHex, orderHashHex,
signedOrder.makerAddress, signedOrder.makerAddress,
@ -128,7 +128,7 @@ describe('MixinSignatureValidator', () => {
it('should revert when SignatureType=Illegal', async () => { it('should revert when SignatureType=Illegal', async () => {
const unsupportedSignatureHex = `0x${SignatureType.Illegal}`; const unsupportedSignatureHex = `0x${SignatureType.Illegal}`;
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
return expectRevertOrOtherErrorAsync( return expectContractCallFailed(
signatureValidator.publicIsValidSignature.callAsync( signatureValidator.publicIsValidSignature.callAsync(
orderHashHex, orderHashHex,
signedOrder.makerAddress, signedOrder.makerAddress,
@ -155,7 +155,7 @@ describe('MixinSignatureValidator', () => {
const signatureBuffer = Buffer.concat([fillerData, signatureType]); const signatureBuffer = Buffer.concat([fillerData, signatureType]);
const signatureHex = ethUtil.bufferToHex(signatureBuffer); const signatureHex = ethUtil.bufferToHex(signatureBuffer);
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
return expectRevertOrOtherErrorAsync( return expectContractCallFailed(
signatureValidator.publicIsValidSignature.callAsync( signatureValidator.publicIsValidSignature.callAsync(
orderHashHex, orderHashHex,
signedOrder.makerAddress, signedOrder.makerAddress,

View File

@ -11,7 +11,7 @@ import { ExchangeContract } from '../../generated_contract_wrappers/exchange';
import { ExchangeWrapperContract } from '../../generated_contract_wrappers/exchange_wrapper'; import { ExchangeWrapperContract } from '../../generated_contract_wrappers/exchange_wrapper';
import { WhitelistContract } from '../../generated_contract_wrappers/whitelist'; import { WhitelistContract } from '../../generated_contract_wrappers/whitelist';
import { artifacts } from '../utils/artifacts'; import { artifacts } from '../utils/artifacts';
import { expectRevertReasonOrAlwaysFailingTransactionAsync } from '../utils/assertions'; import { expectTransactionFailedAsync } from '../utils/assertions';
import { chaiSetup } from '../utils/chai_setup'; import { chaiSetup } from '../utils/chai_setup';
import { constants } from '../utils/constants'; import { constants } from '../utils/constants';
import { ERC20Wrapper } from '../utils/erc20_wrapper'; import { ERC20Wrapper } from '../utils/erc20_wrapper';
@ -134,7 +134,7 @@ describe('Exchange transactions', () => {
}); });
it('should throw if not called by specified sender', async () => { it('should throw if not called by specified sender', async () => {
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.executeTransactionAsync(signedTx, takerAddress), exchangeWrapper.executeTransactionAsync(signedTx, takerAddress),
RevertReason.FailedExecution, RevertReason.FailedExecution,
); );
@ -177,7 +177,7 @@ describe('Exchange transactions', () => {
it('should throw if the a 0x transaction with the same transactionHash has already been executed', async () => { it('should throw if the a 0x transaction with the same transactionHash has already been executed', async () => {
await exchangeWrapper.executeTransactionAsync(signedTx, senderAddress); await exchangeWrapper.executeTransactionAsync(signedTx, senderAddress);
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.executeTransactionAsync(signedTx, senderAddress), exchangeWrapper.executeTransactionAsync(signedTx, senderAddress),
RevertReason.InvalidTxHash, RevertReason.InvalidTxHash,
); );
@ -197,7 +197,7 @@ describe('Exchange transactions', () => {
}); });
it('should throw if not called by specified sender', async () => { it('should throw if not called by specified sender', async () => {
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.executeTransactionAsync(signedTx, makerAddress), exchangeWrapper.executeTransactionAsync(signedTx, makerAddress),
RevertReason.FailedExecution, RevertReason.FailedExecution,
); );
@ -205,7 +205,7 @@ describe('Exchange transactions', () => {
it('should cancel the order when signed by maker and called by sender', async () => { it('should cancel the order when signed by maker and called by sender', async () => {
await exchangeWrapper.executeTransactionAsync(signedTx, senderAddress); await exchangeWrapper.executeTransactionAsync(signedTx, senderAddress);
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.fillOrderAsync(signedOrder, senderAddress), exchangeWrapper.fillOrderAsync(signedOrder, senderAddress),
RevertReason.OrderUnfillable, RevertReason.OrderUnfillable,
); );
@ -250,7 +250,7 @@ describe('Exchange transactions', () => {
signedOrder.signature, signedOrder.signature,
); );
const signedFillTx = takerTransactionFactory.newSignedTransaction(fillData); const signedFillTx = takerTransactionFactory.newSignedTransaction(fillData);
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapperContract.fillOrder.sendTransactionAsync( exchangeWrapperContract.fillOrder.sendTransactionAsync(
orderWithoutExchangeAddress, orderWithoutExchangeAddress,
takerAssetFillAmount, takerAssetFillAmount,
@ -370,7 +370,7 @@ describe('Exchange transactions', () => {
orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder); orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder);
const takerAssetFillAmount = signedOrder.takerAssetAmount; const takerAssetFillAmount = signedOrder.takerAssetAmount;
const salt = generatePseudoRandomSalt(); const salt = generatePseudoRandomSalt();
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
whitelist.fillOrderIfWhitelisted.sendTransactionAsync( whitelist.fillOrderIfWhitelisted.sendTransactionAsync(
orderWithoutExchangeAddress, orderWithoutExchangeAddress,
takerAssetFillAmount, takerAssetFillAmount,
@ -392,7 +392,7 @@ describe('Exchange transactions', () => {
orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder); orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder);
const takerAssetFillAmount = signedOrder.takerAssetAmount; const takerAssetFillAmount = signedOrder.takerAssetAmount;
const salt = generatePseudoRandomSalt(); const salt = generatePseudoRandomSalt();
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
whitelist.fillOrderIfWhitelisted.sendTransactionAsync( whitelist.fillOrderIfWhitelisted.sendTransactionAsync(
orderWithoutExchangeAddress, orderWithoutExchangeAddress,
takerAssetFillAmount, takerAssetFillAmount,

View File

@ -12,7 +12,7 @@ import { ERC20ProxyContract } from '../../generated_contract_wrappers/e_r_c20_pr
import { ERC721ProxyContract } from '../../generated_contract_wrappers/e_r_c721_proxy'; import { ERC721ProxyContract } from '../../generated_contract_wrappers/e_r_c721_proxy';
import { ExchangeContract } from '../../generated_contract_wrappers/exchange'; import { ExchangeContract } from '../../generated_contract_wrappers/exchange';
import { artifacts } from '../utils/artifacts'; import { artifacts } from '../utils/artifacts';
import { expectRevertReasonOrAlwaysFailingTransactionAsync } from '../utils/assertions'; import { expectTransactionFailedAsync } from '../utils/assertions';
import { chaiSetup } from '../utils/chai_setup'; import { chaiSetup } from '../utils/chai_setup';
import { constants } from '../utils/constants'; import { constants } from '../utils/constants';
import { ERC20Wrapper } from '../utils/erc20_wrapper'; import { ERC20Wrapper } from '../utils/erc20_wrapper';
@ -174,7 +174,7 @@ describe('Exchange wrappers', () => {
expirationTimeSeconds: new BigNumber(Math.floor((Date.now() - 10000) / 1000)), expirationTimeSeconds: new BigNumber(Math.floor((Date.now() - 10000) / 1000)),
}); });
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.fillOrKillOrderAsync(signedOrder, takerAddress), exchangeWrapper.fillOrKillOrderAsync(signedOrder, takerAddress),
RevertReason.OrderUnfillable, RevertReason.OrderUnfillable,
); );
@ -187,7 +187,7 @@ describe('Exchange wrappers', () => {
takerAssetFillAmount: signedOrder.takerAssetAmount.div(2), takerAssetFillAmount: signedOrder.takerAssetAmount.div(2),
}); });
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.fillOrKillOrderAsync(signedOrder, takerAddress), exchangeWrapper.fillOrKillOrderAsync(signedOrder, takerAddress),
RevertReason.CompleteFillFailed, RevertReason.CompleteFillFailed,
); );
@ -500,7 +500,7 @@ describe('Exchange wrappers', () => {
await exchangeWrapper.fillOrKillOrderAsync(signedOrders[0], takerAddress); await exchangeWrapper.fillOrKillOrderAsync(signedOrders[0], takerAddress);
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.batchFillOrKillOrdersAsync(signedOrders, takerAddress, { exchangeWrapper.batchFillOrKillOrdersAsync(signedOrders, takerAddress, {
takerAssetFillAmounts, takerAssetFillAmounts,
}), }),
@ -703,7 +703,7 @@ describe('Exchange wrappers', () => {
orderFactory.newSignedOrder(), orderFactory.newSignedOrder(),
]; ];
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.marketSellOrdersAsync(signedOrders, takerAddress, { exchangeWrapper.marketSellOrdersAsync(signedOrders, takerAddress, {
takerAssetFillAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), 18), takerAssetFillAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), 18),
}), }),
@ -921,7 +921,7 @@ describe('Exchange wrappers', () => {
orderFactory.newSignedOrder(), orderFactory.newSignedOrder(),
]; ];
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
exchangeWrapper.marketBuyOrdersAsync(signedOrders, takerAddress, { exchangeWrapper.marketBuyOrdersAsync(signedOrders, takerAddress, {
makerAssetFillAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), 18), makerAssetFillAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), 18),
}), }),

View File

@ -9,7 +9,7 @@ import * as _ from 'lodash';
import { TestLibBytesContract } from '../../generated_contract_wrappers/test_lib_bytes'; import { TestLibBytesContract } from '../../generated_contract_wrappers/test_lib_bytes';
import { artifacts } from '../utils/artifacts'; import { artifacts } from '../utils/artifacts';
import { expectRevertOrOtherErrorAsync } from '../utils/assertions'; import { expectContractCallFailed } from '../utils/assertions';
import { chaiSetup } from '../utils/chai_setup'; import { chaiSetup } from '../utils/chai_setup';
import { constants } from '../utils/constants'; import { constants } from '../utils/constants';
import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper'; import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper';
@ -100,7 +100,7 @@ describe('LibBytes', () => {
describe('popLastByte', () => { describe('popLastByte', () => {
it('should revert if length is 0', async () => { it('should revert if length is 0', async () => {
return expectRevertOrOtherErrorAsync( return expectContractCallFailed(
libBytes.publicPopLastByte.callAsync(constants.NULL_BYTES), libBytes.publicPopLastByte.callAsync(constants.NULL_BYTES),
RevertReason.LibBytesGreaterThanZeroLengthRequired, RevertReason.LibBytesGreaterThanZeroLengthRequired,
); );
@ -116,7 +116,7 @@ describe('LibBytes', () => {
describe('popLast20Bytes', () => { describe('popLast20Bytes', () => {
it('should revert if length is less than 20', async () => { it('should revert if length is less than 20', async () => {
return expectRevertOrOtherErrorAsync( return expectContractCallFailed(
libBytes.publicPopLast20Bytes.callAsync(byteArrayShorterThan20Bytes), libBytes.publicPopLast20Bytes.callAsync(byteArrayShorterThan20Bytes),
RevertReason.LibBytesGreaterOrEqualTo20LengthRequired, RevertReason.LibBytesGreaterOrEqualTo20LengthRequired,
); );
@ -184,7 +184,7 @@ describe('LibBytes', () => {
describe('deepCopyBytes', () => { describe('deepCopyBytes', () => {
it('should revert if dest is shorter than source', async () => { it('should revert if dest is shorter than source', async () => {
return expectRevertOrOtherErrorAsync( return expectContractCallFailed(
libBytes.publicDeepCopyBytes.callAsync(byteArrayShorterThan32Bytes, byteArrayLongerThan32Bytes), libBytes.publicDeepCopyBytes.callAsync(byteArrayShorterThan32Bytes, byteArrayLongerThan32Bytes),
RevertReason.LibBytesGreaterOrEqualToSourceBytesLengthRequired, RevertReason.LibBytesGreaterOrEqualToSourceBytesLengthRequired,
); );
@ -237,7 +237,7 @@ describe('LibBytes', () => {
it('should fail if the byte array is too short to hold an address', async () => { it('should fail if the byte array is too short to hold an address', async () => {
const shortByteArray = '0xabcdef'; const shortByteArray = '0xabcdef';
const offset = new BigNumber(0); const offset = new BigNumber(0);
return expectRevertOrOtherErrorAsync( return expectContractCallFailed(
libBytes.publicReadAddress.callAsync(shortByteArray, offset), libBytes.publicReadAddress.callAsync(shortByteArray, offset),
RevertReason.LibBytesGreaterOrEqualTo20LengthRequired, RevertReason.LibBytesGreaterOrEqualTo20LengthRequired,
); );
@ -245,7 +245,7 @@ describe('LibBytes', () => {
it('should fail if the length between the offset and end of the byte array is too short to hold an address', async () => { it('should fail if the length between the offset and end of the byte array is too short to hold an address', async () => {
const byteArray = testAddress; const byteArray = testAddress;
const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength); const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength);
return expectRevertOrOtherErrorAsync( return expectContractCallFailed(
libBytes.publicReadAddress.callAsync(byteArray, badOffset), libBytes.publicReadAddress.callAsync(byteArray, badOffset),
RevertReason.LibBytesGreaterOrEqualTo20LengthRequired, RevertReason.LibBytesGreaterOrEqualTo20LengthRequired,
); );
@ -281,7 +281,7 @@ describe('LibBytes', () => {
}); });
it('should fail if the byte array is too short to hold an address', async () => { it('should fail if the byte array is too short to hold an address', async () => {
const offset = new BigNumber(0); const offset = new BigNumber(0);
return expectRevertOrOtherErrorAsync( return expectContractCallFailed(
libBytes.publicWriteAddress.callAsync(byteArrayShorterThan20Bytes, offset, testAddress), libBytes.publicWriteAddress.callAsync(byteArrayShorterThan20Bytes, offset, testAddress),
RevertReason.LibBytesGreaterOrEqualTo20LengthRequired, RevertReason.LibBytesGreaterOrEqualTo20LengthRequired,
); );
@ -289,7 +289,7 @@ describe('LibBytes', () => {
it('should fail if the length between the offset and end of the byte array is too short to hold an address', async () => { it('should fail if the length between the offset and end of the byte array is too short to hold an address', async () => {
const byteArray = byteArrayLongerThan32Bytes; const byteArray = byteArrayLongerThan32Bytes;
const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength); const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength);
return expectRevertOrOtherErrorAsync( return expectContractCallFailed(
libBytes.publicWriteAddress.callAsync(byteArray, badOffset, testAddress), libBytes.publicWriteAddress.callAsync(byteArray, badOffset, testAddress),
RevertReason.LibBytesGreaterOrEqualTo20LengthRequired, RevertReason.LibBytesGreaterOrEqualTo20LengthRequired,
); );
@ -313,14 +313,14 @@ describe('LibBytes', () => {
}); });
it('should fail if the byte array is too short to hold a bytes32', async () => { it('should fail if the byte array is too short to hold a bytes32', async () => {
const offset = new BigNumber(0); const offset = new BigNumber(0);
return expectRevertOrOtherErrorAsync( return expectContractCallFailed(
libBytes.publicReadBytes32.callAsync(byteArrayShorterThan32Bytes, offset), libBytes.publicReadBytes32.callAsync(byteArrayShorterThan32Bytes, offset),
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, RevertReason.LibBytesGreaterOrEqualTo32LengthRequired,
); );
}); });
it('should fail if the length between the offset and end of the byte array is too short to hold a bytes32', async () => { it('should fail if the length between the offset and end of the byte array is too short to hold a bytes32', async () => {
const badOffset = new BigNumber(ethUtil.toBuffer(testBytes32).byteLength); const badOffset = new BigNumber(ethUtil.toBuffer(testBytes32).byteLength);
return expectRevertOrOtherErrorAsync( return expectContractCallFailed(
libBytes.publicReadBytes32.callAsync(testBytes32, badOffset), libBytes.publicReadBytes32.callAsync(testBytes32, badOffset),
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, RevertReason.LibBytesGreaterOrEqualTo32LengthRequired,
); );
@ -356,7 +356,7 @@ describe('LibBytes', () => {
}); });
it('should fail if the byte array is too short to hold a bytes32', async () => { it('should fail if the byte array is too short to hold a bytes32', async () => {
const offset = new BigNumber(0); const offset = new BigNumber(0);
return expectRevertOrOtherErrorAsync( return expectContractCallFailed(
libBytes.publicWriteBytes32.callAsync(byteArrayShorterThan32Bytes, offset, testBytes32), libBytes.publicWriteBytes32.callAsync(byteArrayShorterThan32Bytes, offset, testBytes32),
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, RevertReason.LibBytesGreaterOrEqualTo32LengthRequired,
); );
@ -364,7 +364,7 @@ describe('LibBytes', () => {
it('should fail if the length between the offset and end of the byte array is too short to hold a bytes32', async () => { it('should fail if the length between the offset and end of the byte array is too short to hold a bytes32', async () => {
const byteArray = byteArrayLongerThan32Bytes; const byteArray = byteArrayLongerThan32Bytes;
const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength); const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength);
return expectRevertOrOtherErrorAsync( return expectContractCallFailed(
libBytes.publicWriteBytes32.callAsync(byteArray, badOffset, testBytes32), libBytes.publicWriteBytes32.callAsync(byteArray, badOffset, testBytes32),
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, RevertReason.LibBytesGreaterOrEqualTo32LengthRequired,
); );
@ -392,7 +392,7 @@ describe('LibBytes', () => {
}); });
it('should fail if the byte array is too short to hold a uint256', async () => { it('should fail if the byte array is too short to hold a uint256', async () => {
const offset = new BigNumber(0); const offset = new BigNumber(0);
return expectRevertOrOtherErrorAsync( return expectContractCallFailed(
libBytes.publicReadUint256.callAsync(byteArrayShorterThan32Bytes, offset), libBytes.publicReadUint256.callAsync(byteArrayShorterThan32Bytes, offset),
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, RevertReason.LibBytesGreaterOrEqualTo32LengthRequired,
); );
@ -402,7 +402,7 @@ describe('LibBytes', () => {
const testUint256AsBuffer = ethUtil.toBuffer(formattedTestUint256); const testUint256AsBuffer = ethUtil.toBuffer(formattedTestUint256);
const byteArray = ethUtil.bufferToHex(testUint256AsBuffer); const byteArray = ethUtil.bufferToHex(testUint256AsBuffer);
const badOffset = new BigNumber(testUint256AsBuffer.byteLength); const badOffset = new BigNumber(testUint256AsBuffer.byteLength);
return expectRevertOrOtherErrorAsync( return expectContractCallFailed(
libBytes.publicReadUint256.callAsync(byteArray, badOffset), libBytes.publicReadUint256.callAsync(byteArray, badOffset),
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, RevertReason.LibBytesGreaterOrEqualTo32LengthRequired,
); );
@ -442,7 +442,7 @@ describe('LibBytes', () => {
}); });
it('should fail if the byte array is too short to hold a uint256', async () => { it('should fail if the byte array is too short to hold a uint256', async () => {
const offset = new BigNumber(0); const offset = new BigNumber(0);
return expectRevertOrOtherErrorAsync( return expectContractCallFailed(
libBytes.publicWriteUint256.callAsync(byteArrayShorterThan32Bytes, offset, testUint256), libBytes.publicWriteUint256.callAsync(byteArrayShorterThan32Bytes, offset, testUint256),
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, RevertReason.LibBytesGreaterOrEqualTo32LengthRequired,
); );
@ -450,7 +450,7 @@ describe('LibBytes', () => {
it('should fail if the length between the offset and end of the byte array is too short to hold a uint256', async () => { it('should fail if the length between the offset and end of the byte array is too short to hold a uint256', async () => {
const byteArray = byteArrayLongerThan32Bytes; const byteArray = byteArrayLongerThan32Bytes;
const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength); const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength);
return expectRevertOrOtherErrorAsync( return expectContractCallFailed(
libBytes.publicWriteUint256.callAsync(byteArray, badOffset, testUint256), libBytes.publicWriteUint256.callAsync(byteArray, badOffset, testUint256),
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, RevertReason.LibBytesGreaterOrEqualTo32LengthRequired,
); );
@ -461,7 +461,7 @@ describe('LibBytes', () => {
// AssertionError: expected promise to be rejected with an error including 'revert' but it was fulfilled with '0x08c379a0' // AssertionError: expected promise to be rejected with an error including 'revert' but it was fulfilled with '0x08c379a0'
it('should revert if byte array has a length < 4', async () => { it('should revert if byte array has a length < 4', async () => {
const byteArrayLessThan4Bytes = '0x010101'; const byteArrayLessThan4Bytes = '0x010101';
return expectRevertOrOtherErrorAsync( return expectContractCallFailed(
libBytes.publicReadBytes4.callAsync(byteArrayLessThan4Bytes, new BigNumber(0)), libBytes.publicReadBytes4.callAsync(byteArrayLessThan4Bytes, new BigNumber(0)),
RevertReason.LibBytesGreaterOrEqualTo4LengthRequired, RevertReason.LibBytesGreaterOrEqualTo4LengthRequired,
); );
@ -516,28 +516,28 @@ describe('LibBytes', () => {
it('should fail if the byte array is too short to hold the length of a nested byte array', async () => { it('should fail if the byte array is too short to hold the length of a nested byte array', async () => {
// The length of the nested array is 32 bytes. By storing less than 32 bytes, a length cannot be read. // The length of the nested array is 32 bytes. By storing less than 32 bytes, a length cannot be read.
const offset = new BigNumber(0); const offset = new BigNumber(0);
return expectRevertOrOtherErrorAsync( return expectContractCallFailed(
libBytes.publicReadBytesWithLength.callAsync(byteArrayShorterThan32Bytes, offset), libBytes.publicReadBytesWithLength.callAsync(byteArrayShorterThan32Bytes, offset),
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, RevertReason.LibBytesGreaterOrEqualTo32LengthRequired,
); );
}); });
it('should fail if we store a nested byte array length, without a nested byte array', async () => { it('should fail if we store a nested byte array length, without a nested byte array', async () => {
const offset = new BigNumber(0); const offset = new BigNumber(0);
return expectRevertOrOtherErrorAsync( return expectContractCallFailed(
libBytes.publicReadBytesWithLength.callAsync(testBytes32, offset), libBytes.publicReadBytesWithLength.callAsync(testBytes32, offset),
RevertReason.LibBytesGreaterOrEqualToNestedBytesLengthRequired, RevertReason.LibBytesGreaterOrEqualToNestedBytesLengthRequired,
); );
}); });
it('should fail if the length between the offset and end of the byte array is too short to hold the length of a nested byte array', async () => { it('should fail if the length between the offset and end of the byte array is too short to hold the length of a nested byte array', async () => {
const badOffset = new BigNumber(ethUtil.toBuffer(byteArrayShorterThan32Bytes).byteLength); const badOffset = new BigNumber(ethUtil.toBuffer(byteArrayShorterThan32Bytes).byteLength);
return expectRevertOrOtherErrorAsync( return expectContractCallFailed(
libBytes.publicReadBytesWithLength.callAsync(byteArrayShorterThan32Bytes, badOffset), libBytes.publicReadBytesWithLength.callAsync(byteArrayShorterThan32Bytes, badOffset),
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, RevertReason.LibBytesGreaterOrEqualTo32LengthRequired,
); );
}); });
it('should fail if the length between the offset and end of the byte array is too short to hold the nested byte array', async () => { it('should fail if the length between the offset and end of the byte array is too short to hold the nested byte array', async () => {
const badOffset = new BigNumber(ethUtil.toBuffer(testBytes32).byteLength); const badOffset = new BigNumber(ethUtil.toBuffer(testBytes32).byteLength);
return expectRevertOrOtherErrorAsync( return expectContractCallFailed(
libBytes.publicReadBytesWithLength.callAsync(testBytes32, badOffset), libBytes.publicReadBytesWithLength.callAsync(testBytes32, badOffset),
RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, RevertReason.LibBytesGreaterOrEqualTo32LengthRequired,
); );
@ -649,7 +649,7 @@ describe('LibBytes', () => {
it('should fail if the byte array is too short to hold the length of a nested byte array', async () => { it('should fail if the byte array is too short to hold the length of a nested byte array', async () => {
const offset = new BigNumber(0); const offset = new BigNumber(0);
const emptyByteArray = ethUtil.bufferToHex(new Buffer(1)); const emptyByteArray = ethUtil.bufferToHex(new Buffer(1));
return expectRevertOrOtherErrorAsync( return expectContractCallFailed(
libBytes.publicWriteBytesWithLength.callAsync(emptyByteArray, offset, longData), libBytes.publicWriteBytesWithLength.callAsync(emptyByteArray, offset, longData),
RevertReason.LibBytesGreaterOrEqualToNestedBytesLengthRequired, RevertReason.LibBytesGreaterOrEqualToNestedBytesLengthRequired,
); );
@ -657,7 +657,7 @@ describe('LibBytes', () => {
it('should fail if the length between the offset and end of the byte array is too short to hold the length of a nested byte array)', async () => { it('should fail if the length between the offset and end of the byte array is too short to hold the length of a nested byte array)', async () => {
const emptyByteArray = ethUtil.bufferToHex(new Buffer(shortTestBytesAsBuffer.byteLength)); const emptyByteArray = ethUtil.bufferToHex(new Buffer(shortTestBytesAsBuffer.byteLength));
const badOffset = new BigNumber(ethUtil.toBuffer(shortTestBytesAsBuffer).byteLength); const badOffset = new BigNumber(ethUtil.toBuffer(shortTestBytesAsBuffer).byteLength);
return expectRevertOrOtherErrorAsync( return expectContractCallFailed(
libBytes.publicWriteBytesWithLength.callAsync(emptyByteArray, badOffset, shortData), libBytes.publicWriteBytesWithLength.callAsync(emptyByteArray, badOffset, shortData),
RevertReason.LibBytesGreaterOrEqualToNestedBytesLengthRequired, RevertReason.LibBytesGreaterOrEqualToNestedBytesLengthRequired,
); );

View File

@ -14,8 +14,9 @@ import { MixinAuthorizableContract } from '../../generated_contract_wrappers/mix
import { TestAssetProxyOwnerContract } from '../../generated_contract_wrappers/test_asset_proxy_owner'; import { TestAssetProxyOwnerContract } from '../../generated_contract_wrappers/test_asset_proxy_owner';
import { artifacts } from '../utils/artifacts'; import { artifacts } from '../utils/artifacts';
import { import {
expectRevertOrAlwaysFailingTransactionAsync, expectContractCallFailedWithoutReasonAsync,
expectRevertOrContractCallFailedAsync, expectContractCreationFailedWithoutReason,
expectTransactionFailedWithoutReasonAsync,
} from '../utils/assertions'; } from '../utils/assertions';
import { chaiSetup } from '../utils/chai_setup'; import { chaiSetup } from '../utils/chai_setup';
import { constants } from '../utils/constants'; import { constants } from '../utils/constants';
@ -108,7 +109,7 @@ describe('AssetProxyOwner', () => {
}); });
it('should throw if a null address is included in assetProxyContracts', async () => { it('should throw if a null address is included in assetProxyContracts', async () => {
const assetProxyContractAddresses = [erc20Proxy.address, constants.NULL_ADDRESS]; const assetProxyContractAddresses = [erc20Proxy.address, constants.NULL_ADDRESS];
return expectRevertOrAlwaysFailingTransactionAsync( return expectContractCreationFailedWithoutReason(
AssetProxyOwnerContract.deployFrom0xArtifactAsync( AssetProxyOwnerContract.deployFrom0xArtifactAsync(
artifacts.AssetProxyOwner, artifacts.AssetProxyOwner,
provider, provider,
@ -150,7 +151,7 @@ describe('AssetProxyOwner', () => {
describe('registerAssetProxy', () => { describe('registerAssetProxy', () => {
it('should throw if not called by multisig', async () => { it('should throw if not called by multisig', async () => {
const isRegistered = true; const isRegistered = true;
return expectRevertOrAlwaysFailingTransactionAsync( return expectTransactionFailedWithoutReasonAsync(
testAssetProxyOwner.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, isRegistered, { testAssetProxyOwner.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, isRegistered, {
from: owners[0], from: owners[0],
}), }),
@ -277,7 +278,7 @@ describe('AssetProxyOwner', () => {
); );
const log = submitTxRes.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>; const log = submitTxRes.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
const txId = log.args.transactionId; const txId = log.args.transactionId;
return expectRevertOrContractCallFailedAsync( return expectContractCallFailedWithoutReasonAsync(
testAssetProxyOwner.testValidRemoveAuthorizedAddressAtIndexTx.callAsync(txId), testAssetProxyOwner.testValidRemoveAuthorizedAddressAtIndexTx.callAsync(txId),
); );
}); });
@ -312,7 +313,7 @@ describe('AssetProxyOwner', () => {
); );
const log = submitTxRes.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>; const log = submitTxRes.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
const txId = log.args.transactionId; const txId = log.args.transactionId;
return expectRevertOrContractCallFailedAsync( return expectContractCallFailedWithoutReasonAsync(
testAssetProxyOwner.testValidRemoveAuthorizedAddressAtIndexTx.callAsync(txId), testAssetProxyOwner.testValidRemoveAuthorizedAddressAtIndexTx.callAsync(txId),
); );
}); });
@ -332,7 +333,7 @@ describe('AssetProxyOwner', () => {
const log = res.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>; const log = res.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
const txId = log.args.transactionId; const txId = log.args.transactionId;
return expectRevertOrAlwaysFailingTransactionAsync( return expectTransactionFailedWithoutReasonAsync(
testAssetProxyOwner.executeRemoveAuthorizedAddressAtIndex.sendTransactionAsync(txId, { testAssetProxyOwner.executeRemoveAuthorizedAddressAtIndex.sendTransactionAsync(txId, {
from: owners[1], from: owners[1],
}), }),
@ -354,7 +355,7 @@ describe('AssetProxyOwner', () => {
await multiSigWrapper.confirmTransactionAsync(txId, owners[1]); await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
return expectRevertOrAlwaysFailingTransactionAsync( return expectTransactionFailedWithoutReasonAsync(
testAssetProxyOwner.executeRemoveAuthorizedAddressAtIndex.sendTransactionAsync(txId, { testAssetProxyOwner.executeRemoveAuthorizedAddressAtIndex.sendTransactionAsync(txId, {
from: owners[1], from: owners[1],
}), }),
@ -376,7 +377,7 @@ describe('AssetProxyOwner', () => {
await multiSigWrapper.confirmTransactionAsync(txId, owners[1]); await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
return expectRevertOrAlwaysFailingTransactionAsync( return expectTransactionFailedWithoutReasonAsync(
testAssetProxyOwner.executeRemoveAuthorizedAddressAtIndex.sendTransactionAsync(txId, { testAssetProxyOwner.executeRemoveAuthorizedAddressAtIndex.sendTransactionAsync(txId, {
from: owners[1], from: owners[1],
}), }),
@ -433,7 +434,7 @@ describe('AssetProxyOwner', () => {
const isExecuted = tx[3]; const isExecuted = tx[3];
expect(isExecuted).to.equal(true); expect(isExecuted).to.equal(true);
return expectRevertOrAlwaysFailingTransactionAsync( return expectTransactionFailedWithoutReasonAsync(
testAssetProxyOwner.executeRemoveAuthorizedAddressAtIndex.sendTransactionAsync(txId, { testAssetProxyOwner.executeRemoveAuthorizedAddressAtIndex.sendTransactionAsync(txId, {
from: owners[1], from: owners[1],
}), }),

View File

@ -8,7 +8,7 @@ import {
SubmissionContractEventArgs, SubmissionContractEventArgs,
} from '../../generated_contract_wrappers/multi_sig_wallet_with_time_lock'; } from '../../generated_contract_wrappers/multi_sig_wallet_with_time_lock';
import { artifacts } from '../utils/artifacts'; import { artifacts } from '../utils/artifacts';
import { expectRevertOrAlwaysFailingTransactionAsync } from '../utils/assertions'; import { expectTransactionFailedWithoutReasonAsync } from '../utils/assertions';
import { chaiSetup } from '../utils/chai_setup'; import { chaiSetup } from '../utils/chai_setup';
import { constants } from '../utils/constants'; import { constants } from '../utils/constants';
import { increaseTimeAndMineBlockAsync } from '../utils/increase_time'; import { increaseTimeAndMineBlockAsync } from '../utils/increase_time';
@ -67,7 +67,7 @@ describe('MultiSigWalletWithTimeLock', () => {
}); });
it('should throw when not called by wallet', async () => { it('should throw when not called by wallet', async () => {
return expectRevertOrAlwaysFailingTransactionAsync( return expectTransactionFailedWithoutReasonAsync(
multiSig.changeTimeLock.sendTransactionAsync(SECONDS_TIME_LOCKED, { from: owners[0] }), multiSig.changeTimeLock.sendTransactionAsync(SECONDS_TIME_LOCKED, { from: owners[0] }),
); );
}); });
@ -78,7 +78,7 @@ describe('MultiSigWalletWithTimeLock', () => {
const res = await multiSigWrapper.submitTransactionAsync(destination, changeTimeLockData, owners[0]); const res = await multiSigWrapper.submitTransactionAsync(destination, changeTimeLockData, owners[0]);
const log = res.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>; const log = res.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
const txId = log.args.transactionId; const txId = log.args.transactionId;
return expectRevertOrAlwaysFailingTransactionAsync( return expectTransactionFailedWithoutReasonAsync(
multiSig.executeTransaction.sendTransactionAsync(txId, { from: owners[0] }), multiSig.executeTransaction.sendTransactionAsync(txId, { from: owners[0] }),
); );
}); });
@ -147,7 +147,7 @@ describe('MultiSigWalletWithTimeLock', () => {
}); });
it('should throw if it has enough confirmations but is not past the time lock', async () => { it('should throw if it has enough confirmations but is not past the time lock', async () => {
return expectRevertOrAlwaysFailingTransactionAsync( return expectTransactionFailedWithoutReasonAsync(
multiSig.executeTransaction.sendTransactionAsync(txId, { from: owners[0] }), multiSig.executeTransaction.sendTransactionAsync(txId, { from: owners[0] }),
); );
}); });

View File

@ -7,7 +7,7 @@ import * as _ from 'lodash';
import { TokenRegistryContract } from '../generated_contract_wrappers/token_registry'; import { TokenRegistryContract } from '../generated_contract_wrappers/token_registry';
import { artifacts } from './utils/artifacts'; import { artifacts } from './utils/artifacts';
import { expectRevertOrAlwaysFailingTransactionAsync } from './utils/assertions'; import { expectTransactionFailedWithoutReasonAsync } from './utils/assertions';
import { chaiSetup } from './utils/chai_setup'; import { chaiSetup } from './utils/chai_setup';
import { constants } from './utils/constants'; import { constants } from './utils/constants';
import { TokenRegWrapper } from './utils/token_registry_wrapper'; import { TokenRegWrapper } from './utils/token_registry_wrapper';
@ -75,7 +75,7 @@ describe('TokenRegistry', () => {
describe('addToken', () => { describe('addToken', () => {
it('should throw when not called by owner', async () => { it('should throw when not called by owner', async () => {
return expectRevertOrAlwaysFailingTransactionAsync(tokenRegWrapper.addTokenAsync(token1, notOwner)); return expectTransactionFailedWithoutReasonAsync(tokenRegWrapper.addTokenAsync(token1, notOwner));
}); });
it('should add token metadata when called by owner', async () => { it('should add token metadata when called by owner', async () => {
@ -87,20 +87,18 @@ describe('TokenRegistry', () => {
it('should throw if token already exists', async () => { it('should throw if token already exists', async () => {
await tokenRegWrapper.addTokenAsync(token1, owner); await tokenRegWrapper.addTokenAsync(token1, owner);
return expectRevertOrAlwaysFailingTransactionAsync(tokenRegWrapper.addTokenAsync(token1, owner)); return expectTransactionFailedWithoutReasonAsync(tokenRegWrapper.addTokenAsync(token1, owner));
}); });
it('should throw if token address is null', async () => { it('should throw if token address is null', async () => {
return expectRevertOrAlwaysFailingTransactionAsync(tokenRegWrapper.addTokenAsync(nullToken, owner)); return expectTransactionFailedWithoutReasonAsync(tokenRegWrapper.addTokenAsync(nullToken, owner));
}); });
it('should throw if name already exists', async () => { it('should throw if name already exists', async () => {
await tokenRegWrapper.addTokenAsync(token1, owner); await tokenRegWrapper.addTokenAsync(token1, owner);
const duplicateNameToken = _.assign({}, token2, { name: token1.name }); const duplicateNameToken = _.assign({}, token2, { name: token1.name });
return expectRevertOrAlwaysFailingTransactionAsync( return expectTransactionFailedWithoutReasonAsync(tokenRegWrapper.addTokenAsync(duplicateNameToken, owner));
tokenRegWrapper.addTokenAsync(duplicateNameToken, owner),
);
}); });
it('should throw if symbol already exists', async () => { it('should throw if symbol already exists', async () => {
@ -109,7 +107,7 @@ describe('TokenRegistry', () => {
symbol: token1.symbol, symbol: token1.symbol,
}); });
return expectRevertOrAlwaysFailingTransactionAsync( return expectTransactionFailedWithoutReasonAsync(
tokenRegWrapper.addTokenAsync(duplicateSymbolToken, owner), tokenRegWrapper.addTokenAsync(duplicateSymbolToken, owner),
); );
}); });
@ -136,7 +134,7 @@ describe('TokenRegistry', () => {
describe('setTokenName', () => { describe('setTokenName', () => {
it('should throw when not called by owner', async () => { it('should throw when not called by owner', async () => {
return expectRevertOrAlwaysFailingTransactionAsync( return expectTransactionFailedWithoutReasonAsync(
tokenReg.setTokenName.sendTransactionAsync(token1.address, token2.name, { from: notOwner }), tokenReg.setTokenName.sendTransactionAsync(token1.address, token2.name, { from: notOwner }),
); );
}); });
@ -162,13 +160,13 @@ describe('TokenRegistry', () => {
it('should throw if the name already exists', async () => { it('should throw if the name already exists', async () => {
await tokenRegWrapper.addTokenAsync(token2, owner); await tokenRegWrapper.addTokenAsync(token2, owner);
return expectRevertOrAlwaysFailingTransactionAsync( return expectTransactionFailedWithoutReasonAsync(
tokenReg.setTokenName.sendTransactionAsync(token1.address, token2.name, { from: owner }), tokenReg.setTokenName.sendTransactionAsync(token1.address, token2.name, { from: owner }),
); );
}); });
it('should throw if token does not exist', async () => { it('should throw if token does not exist', async () => {
return expectRevertOrAlwaysFailingTransactionAsync( return expectTransactionFailedWithoutReasonAsync(
tokenReg.setTokenName.sendTransactionAsync(nullToken.address, token2.name, { from: owner }), tokenReg.setTokenName.sendTransactionAsync(nullToken.address, token2.name, { from: owner }),
); );
}); });
@ -176,7 +174,7 @@ describe('TokenRegistry', () => {
describe('setTokenSymbol', () => { describe('setTokenSymbol', () => {
it('should throw when not called by owner', async () => { it('should throw when not called by owner', async () => {
return expectRevertOrAlwaysFailingTransactionAsync( return expectTransactionFailedWithoutReasonAsync(
tokenReg.setTokenSymbol.sendTransactionAsync(token1.address, token2.symbol, { tokenReg.setTokenSymbol.sendTransactionAsync(token1.address, token2.symbol, {
from: notOwner, from: notOwner,
}), }),
@ -202,7 +200,7 @@ describe('TokenRegistry', () => {
it('should throw if the symbol already exists', async () => { it('should throw if the symbol already exists', async () => {
await tokenRegWrapper.addTokenAsync(token2, owner); await tokenRegWrapper.addTokenAsync(token2, owner);
return expectRevertOrAlwaysFailingTransactionAsync( return expectTransactionFailedWithoutReasonAsync(
tokenReg.setTokenSymbol.sendTransactionAsync(token1.address, token2.symbol, { tokenReg.setTokenSymbol.sendTransactionAsync(token1.address, token2.symbol, {
from: owner, from: owner,
}), }),
@ -210,7 +208,7 @@ describe('TokenRegistry', () => {
}); });
it('should throw if token does not exist', async () => { it('should throw if token does not exist', async () => {
return expectRevertOrAlwaysFailingTransactionAsync( return expectTransactionFailedWithoutReasonAsync(
tokenReg.setTokenSymbol.sendTransactionAsync(nullToken.address, token2.symbol, { tokenReg.setTokenSymbol.sendTransactionAsync(nullToken.address, token2.symbol, {
from: owner, from: owner,
}), }),
@ -221,7 +219,7 @@ describe('TokenRegistry', () => {
describe('removeToken', () => { describe('removeToken', () => {
it('should throw if not called by owner', async () => { it('should throw if not called by owner', async () => {
const index = new BigNumber(0); const index = new BigNumber(0);
return expectRevertOrAlwaysFailingTransactionAsync( return expectTransactionFailedWithoutReasonAsync(
tokenReg.removeToken.sendTransactionAsync(token1.address, index, { from: notOwner }), tokenReg.removeToken.sendTransactionAsync(token1.address, index, { from: notOwner }),
); );
}); });
@ -240,7 +238,7 @@ describe('TokenRegistry', () => {
it('should throw if token does not exist', async () => { it('should throw if token does not exist', async () => {
const index = new BigNumber(0); const index = new BigNumber(0);
return expectRevertOrAlwaysFailingTransactionAsync( return expectTransactionFailedWithoutReasonAsync(
tokenReg.removeToken.sendTransactionAsync(nullToken.address, index, { from: owner }), tokenReg.removeToken.sendTransactionAsync(nullToken.address, index, { from: owner }),
); );
}); });
@ -248,7 +246,7 @@ describe('TokenRegistry', () => {
it('should throw if token at given index does not match address', async () => { it('should throw if token at given index does not match address', async () => {
await tokenRegWrapper.addTokenAsync(token2, owner); await tokenRegWrapper.addTokenAsync(token2, owner);
const incorrectIndex = new BigNumber(0); const incorrectIndex = new BigNumber(0);
return expectRevertOrAlwaysFailingTransactionAsync( return expectTransactionFailedWithoutReasonAsync(
tokenReg.removeToken.sendTransactionAsync(token2.address, incorrectIndex, { from: owner }), tokenReg.removeToken.sendTransactionAsync(token2.address, incorrectIndex, { from: owner }),
); );
}); });

View File

@ -5,7 +5,7 @@ import * as chai from 'chai';
import { WETH9Contract } from '../../generated_contract_wrappers/weth9'; import { WETH9Contract } from '../../generated_contract_wrappers/weth9';
import { artifacts } from '../utils/artifacts'; import { artifacts } from '../utils/artifacts';
import { expectInsufficientFundsAsync, expectRevertOrAlwaysFailingTransactionAsync } from '../utils/assertions'; import { expectInsufficientFundsAsync, expectTransactionFailedWithoutReasonAsync } from '../utils/assertions';
import { chaiSetup } from '../utils/chai_setup'; import { chaiSetup } from '../utils/chai_setup';
import { constants } from '../utils/constants'; import { constants } from '../utils/constants';
import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper'; import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper';
@ -74,7 +74,7 @@ describe('EtherToken', () => {
const initEthTokenBalance = await etherToken.balanceOf.callAsync(account); const initEthTokenBalance = await etherToken.balanceOf.callAsync(account);
const ethTokensToWithdraw = initEthTokenBalance.plus(1); const ethTokensToWithdraw = initEthTokenBalance.plus(1);
return expectRevertOrAlwaysFailingTransactionAsync( return expectTransactionFailedWithoutReasonAsync(
etherToken.withdraw.sendTransactionAsync(ethTokensToWithdraw), etherToken.withdraw.sendTransactionAsync(ethTokensToWithdraw),
); );
}); });

View File

@ -5,7 +5,7 @@ import * as chai from 'chai';
import { DummyERC20TokenContract } from '../../generated_contract_wrappers/dummy_e_r_c20_token'; import { DummyERC20TokenContract } from '../../generated_contract_wrappers/dummy_e_r_c20_token';
import { artifacts } from '../utils/artifacts'; import { artifacts } from '../utils/artifacts';
import { expectRevertOrOtherErrorAsync } from '../utils/assertions'; import { expectContractCallFailed } from '../utils/assertions';
import { chaiSetup } from '../utils/chai_setup'; import { chaiSetup } from '../utils/chai_setup';
import { constants } from '../utils/constants'; import { constants } from '../utils/constants';
import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper'; import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper';
@ -54,7 +54,7 @@ describe('UnlimitedAllowanceToken', () => {
it('should throw if owner has insufficient balance', async () => { it('should throw if owner has insufficient balance', async () => {
const ownerBalance = await token.balanceOf.callAsync(owner); const ownerBalance = await token.balanceOf.callAsync(owner);
const amountToTransfer = ownerBalance.plus(1); const amountToTransfer = ownerBalance.plus(1);
return expectRevertOrOtherErrorAsync( return expectContractCallFailed(
token.transfer.callAsync(spender, amountToTransfer, { from: owner }), token.transfer.callAsync(spender, amountToTransfer, { from: owner }),
RevertReason.Erc20InsufficientBalance, RevertReason.Erc20InsufficientBalance,
); );
@ -93,7 +93,7 @@ describe('UnlimitedAllowanceToken', () => {
await token.approve.sendTransactionAsync(spender, amountToTransfer, { from: owner }), await token.approve.sendTransactionAsync(spender, amountToTransfer, { from: owner }),
constants.AWAIT_TRANSACTION_MINED_MS, constants.AWAIT_TRANSACTION_MINED_MS,
); );
return expectRevertOrOtherErrorAsync( return expectContractCallFailed(
token.transferFrom.callAsync(owner, spender, amountToTransfer, { token.transferFrom.callAsync(owner, spender, amountToTransfer, {
from: spender, from: spender,
}), }),
@ -109,7 +109,7 @@ describe('UnlimitedAllowanceToken', () => {
const isSpenderAllowanceInsufficient = spenderAllowance.cmp(amountToTransfer) < 0; const isSpenderAllowanceInsufficient = spenderAllowance.cmp(amountToTransfer) < 0;
expect(isSpenderAllowanceInsufficient).to.be.true(); expect(isSpenderAllowanceInsufficient).to.be.true();
return expectRevertOrOtherErrorAsync( return expectContractCallFailed(
token.transferFrom.callAsync(owner, spender, amountToTransfer, { token.transferFrom.callAsync(owner, spender, amountToTransfer, {
from: spender, from: spender,
}), }),

View File

@ -1,108 +1,159 @@
import { RevertReason } from '@0xproject/types'; import { RevertReason } from '@0xproject/types';
import { logUtils } from '@0xproject/utils';
import { NodeType } from '@0xproject/web3-wrapper';
import * as chai from 'chai'; import * as chai from 'chai';
import { TransactionReceipt, TransactionReceiptWithDecodedLogs } from 'ethereum-types'; import { TransactionReceipt, TransactionReceiptStatus, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import * as _ from 'lodash'; import * as _ from 'lodash';
import { constants } from './constants';
import { web3Wrapper } from './web3_wrapper'; import { web3Wrapper } from './web3_wrapper';
const expect = chai.expect; const expect = chai.expect;
function _expectEitherErrorAsync<T>(p: Promise<T>, error1: string, error2: string): PromiseLike<void> { let nodeType: NodeType | undefined;
return expect(p)
.to.be.rejected() // Represents the return value of a `sendTransaction` call. The Promise should
.then(e => { // resolve with either a transaction receipt or a transaction hash.
expect(e).to.satisfy( export type sendTransactionResult = Promise<TransactionReceipt | TransactionReceiptWithDecodedLogs | string>;
(err: Error) => _.includes(err.message, error1) || _.includes(err.message, error2),
`expected promise to reject with error message that includes "${error1}" or "${error2}", but got: ` + async function _getGanacheOrGethError(ganacheError: string, gethError: string): Promise<string> {
`"${e.message}"\n`, if (_.isUndefined(nodeType)) {
); nodeType = await web3Wrapper.getNodeTypeAsync();
}); }
switch (nodeType) {
case NodeType.Ganache:
return ganacheError;
case NodeType.Geth:
return gethError;
default:
throw new Error(`Unknown node type: ${nodeType}`);
}
}
async function _getInsufficientFundsErrorMessageAsync(): Promise<string> {
return _getGanacheOrGethError("sender doesn't have enough funds", 'insufficient funds');
}
async function _getTransactionFailedErrorMessageAsync(): Promise<string> {
return _getGanacheOrGethError('revert', 'always failing transaction');
}
async function _getContractCallFailedErrorMessageAsync(): Promise<string> {
return _getGanacheOrGethError('revert', 'Contract call failed');
} }
/** /**
* Rejects if the given Promise does not reject with an error indicating * Rejects if the given Promise does not reject with an error indicating
* insufficient funds. * insufficient funds.
* @param p the Promise which is expected to reject * @param p a promise resulting from a contract call or sendTransaction call.
* @returns a new Promise which will reject if the conditions are not met and * @returns a new Promise which will reject if the conditions are not met and
* otherwise resolve with no value. * otherwise resolve with no value.
*/ */
export function expectInsufficientFundsAsync<T>(p: Promise<T>): PromiseLike<void> { export async function expectInsufficientFundsAsync<T>(p: Promise<T>): Promise<void> {
return _expectEitherErrorAsync(p, 'insufficient funds', "sender doesn't have enough funds"); const errMessage = await _getInsufficientFundsErrorMessageAsync();
return expect(p).to.be.rejectedWith(errMessage);
} }
/** /**
* Rejects if the given Promise does not reject with a "revert" error or the * Resolves if the the sendTransaction call fails with the given revert reason.
* given otherError. * However, since Geth does not support revert reasons for sendTransaction, this
* @param p the Promise which is expected to reject * falls back to expectTransactionFailedWithoutReasonAsync if the backing
* @param otherError the other error which is accepted as a valid reject error. * Ethereum node is Geth.
* @returns a new Promise which will reject if the conditions are not met and * @param p a Promise resulting from a sendTransaction call
* otherwise resolve with no value.
*/
export function expectRevertOrOtherErrorAsync<T>(p: Promise<T>, otherError: string): PromiseLike<void> {
return _expectEitherErrorAsync(p, constants.REVERT, otherError);
}
/**
* Rejects if the given Promise does not reject with a "revert" or "always
* failing transaction" error.
* @param p the Promise which is expected to reject
* @returns a new Promise which will reject if the conditions are not met and
* otherwise resolve with no value.
*/
export function expectRevertOrAlwaysFailingTransactionAsync<T>(p: Promise<T>): PromiseLike<void> {
return expectRevertOrOtherErrorAsync(p, 'always failing transaction');
}
/**
* Rejects if at least one the following conditions is not met:
* 1) The given Promise rejects with the given revert reason.
* 2) The given Promise rejects with an error containing "always failing transaction"
* 3) The given Promise fulfills with a txReceipt that has a status of 0 or '0', indicating the transaction failed.
* 4) The given Promise fulfills with a txHash and corresponding txReceipt has a status of 0 or '0'.
* @param p the Promise which is expected to reject
* @param reason a specific revert reason * @param reason a specific revert reason
* @returns a new Promise which will reject if the conditions are not met and * @returns a new Promise which will reject if the conditions are not met and
* otherwise resolve with no value. * otherwise resolve with no value.
*/ */
export async function expectRevertReasonOrAlwaysFailingTransactionAsync( export async function expectTransactionFailedAsync(p: sendTransactionResult, reason: RevertReason): Promise<void> {
p: Promise<string | TransactionReceiptWithDecodedLogs | TransactionReceipt>, // HACK(albrow): This dummy `catch` should not be necessary, but if you
reason: RevertReason, // remove it, there is an uncaught exception and the Node process will
): Promise<void> { // forcibly exit. It's possible this is a false positive in
// make-promises-safe.
p.catch(e => {
_.noop(e);
});
if (_.isUndefined(nodeType)) {
nodeType = await web3Wrapper.getNodeTypeAsync();
}
switch (nodeType) {
case NodeType.Ganache:
return expect(p).to.be.rejectedWith(reason);
case NodeType.Geth:
logUtils.warn(
'WARNING: Geth does not support revert reasons for sendTransaction. This test will pass if the transaction fails for any reason.',
);
return expectTransactionFailedWithoutReasonAsync(p);
default:
throw new Error(`Unknown node type: ${nodeType}`);
}
}
/**
* Resolves if the transaction fails without a revert reason, or if the
* corresponding transactionReceipt has a status of 0 or '0', indicating
* failure.
* @param p a Promise resulting from a sendTransaction call
* @returns a new Promise which will reject if the conditions are not met and
* otherwise resolve with no value.
*/
export async function expectTransactionFailedWithoutReasonAsync(p: sendTransactionResult): Promise<void> {
return p return p
.then(async result => { .then(async result => {
let txReceiptStatus: string | 0 | 1 | null; let txReceiptStatus: TransactionReceiptStatus;
if (typeof result === 'string') { if (_.isString(result)) {
// Result is a txHash. We need to make a web3 call to get the receipt. // Result is a txHash. We need to make a web3 call to get the
// receipt, then get the status from the receipt.
const txReceipt = await web3Wrapper.awaitTransactionMinedAsync(result); const txReceipt = await web3Wrapper.awaitTransactionMinedAsync(result);
txReceiptStatus = txReceipt.status; txReceiptStatus = txReceipt.status;
} else if ('status' in result) { } else if ('status' in result) {
// Result is a TransactionReceiptWithDecodedLogs or TransactionReceipt // Result is a transaction receipt, so we can get the status
// and status is a field of result. // directly.
txReceiptStatus = result.status; txReceiptStatus = result.status;
} else { } else {
throw new Error('Unexpected result type'); throw new Error('Unexpected result type: ' + typeof result);
} }
expect(_.toString(txReceiptStatus)).to.equal( expect(_.toString(txReceiptStatus)).to.equal(
'0', '0',
'transactionReceipt had a non-zero status, indicating success', 'Expected transaction to fail but receipt had a non-zero status, indicating success',
); );
}) })
.catch(err => { .catch(async err => {
expect(err.message).to.satisfy( // If the promise rejects, we expect a specific error message,
(msg: string) => _.includes(msg, reason) || _.includes(msg, 'always failing transaction'), // depending on the backing Ethereum node type.
`Expected ${reason} or 'always failing transaction' but error message was ${err.message}`, const errMessage = await _getTransactionFailedErrorMessageAsync();
); expect(err.message).to.include(errMessage);
}); });
} }
/** /**
* Rejects if the given Promise does not reject with a "revert" or "Contract * Resolves if the the contract call fails with the given revert reason.
* call failed" error. * @param p a Promise resulting from a contract call
* @param p the Promise which is expected to reject * @param reason a specific revert reason
* @returns a new Promise which will reject if the conditions are not met and * @returns a new Promise which will reject if the conditions are not met and
* otherwise resolve with no value. * otherwise resolve with no value.
*/ */
export function expectRevertOrContractCallFailedAsync<T>(p: Promise<T>): PromiseLike<void> { export async function expectContractCallFailed<T>(p: Promise<T>, reason: RevertReason): Promise<void> {
return expectRevertOrOtherErrorAsync<T>(p, 'Contract call failed'); return expect(p).to.be.rejectedWith(reason);
}
/**
* Resolves if the contract call fails without a revert reason.
* @param p a Promise resulting from a contract call
* @returns a new Promise which will reject if the conditions are not met and
* otherwise resolve with no value.
*/
export async function expectContractCallFailedWithoutReasonAsync<T>(p: Promise<T>): Promise<void> {
const errMessage = await _getContractCallFailedErrorMessageAsync();
return expect(p).to.be.rejectedWith(errMessage);
}
/**
* Resolves if the contract creation/deployment fails without a revert reason.
* @param p a Promise resulting from a contract creation/deployment
* @returns a new Promise which will reject if the conditions are not met and
* otherwise resolve with no value.
*/
export async function expectContractCreationFailedWithoutReason<T>(p: Promise<T>): Promise<void> {
const errMessage = await _getTransactionFailedErrorMessageAsync();
return expect(p).to.be.rejectedWith(errMessage);
} }

View File

@ -18,7 +18,6 @@ const TESTRPC_PRIVATE_KEYS_STRINGS = [
export const constants = { export const constants = {
INVALID_OPCODE: 'invalid opcode', INVALID_OPCODE: 'invalid opcode',
REVERT: 'revert',
TESTRPC_NETWORK_ID: 50, TESTRPC_NETWORK_ID: 50,
// Note(albrow): In practice V8 and most other engines limit the minimum // Note(albrow): In practice V8 and most other engines limit the minimum
// interval for setInterval to 10ms. We still set it to 0 here in order to // interval for setInterval to 10ms. We still set it to 0 here in order to

View File

@ -17,7 +17,7 @@ import 'make-promises-safe';
import { ExchangeContract, FillContractEventArgs } from '../../generated_contract_wrappers/exchange'; import { ExchangeContract, FillContractEventArgs } from '../../generated_contract_wrappers/exchange';
import { artifacts } from './artifacts'; import { artifacts } from './artifacts';
import { expectRevertReasonOrAlwaysFailingTransactionAsync } from './assertions'; import { expectTransactionFailedAsync } from './assertions';
import { AssetWrapper } from './asset_wrapper'; import { AssetWrapper } from './asset_wrapper';
import { chaiSetup } from './chai_setup'; import { chaiSetup } from './chai_setup';
import { constants } from './constants'; import { constants } from './constants';
@ -75,7 +75,7 @@ export async function coreCombinatorialUtilsFactoryAsync(
const zrxAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address); const zrxAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address);
const erc20FiveDecimalTokenCount = 2; const erc20FiveDecimalTokenCount = 2;
const fiveDecimals = new BigNumber(18); const fiveDecimals = new BigNumber(5);
const [erc20FiveDecimalTokenA, erc20FiveDecimalTokenB] = await erc20Wrapper.deployDummyTokensAsync( const [erc20FiveDecimalTokenA, erc20FiveDecimalTokenB] = await erc20Wrapper.deployDummyTokensAsync(
erc20FiveDecimalTokenCount, erc20FiveDecimalTokenCount,
fiveDecimals, fiveDecimals,
@ -418,7 +418,7 @@ export class CoreCombinatorialUtils {
fillRevertReasonIfExists: RevertReason | undefined, fillRevertReasonIfExists: RevertReason | undefined,
): Promise<void> { ): Promise<void> {
if (!_.isUndefined(fillRevertReasonIfExists)) { if (!_.isUndefined(fillRevertReasonIfExists)) {
return expectRevertReasonOrAlwaysFailingTransactionAsync( return expectTransactionFailedAsync(
this.exchangeWrapper.fillOrderAsync(signedOrder, this.takerAddress, { takerAssetFillAmount }), this.exchangeWrapper.fillOrderAsync(signedOrder, this.takerAddress, { takerAssetFillAmount }),
fillRevertReasonIfExists, fillRevertReasonIfExists,
); );

View File

@ -48,12 +48,11 @@
"@0xproject/subproviders": "^0.10.4", "@0xproject/subproviders": "^0.10.4",
"@0xproject/types": "^0.8.1", "@0xproject/types": "^0.8.1",
"@0xproject/utils": "^0.7.1", "@0xproject/utils": "^0.7.1",
"ethereum-types": "^0.0.1", "ethereum-types": "^0.0.2",
"@0xproject/typescript-typings": "^0.4.1", "@0xproject/typescript-typings": "^0.4.1",
"@0xproject/web3-wrapper": "^0.7.1", "@0xproject/web3-wrapper": "^0.7.1",
"lodash": "^4.17.4", "lodash": "^4.17.4",
"web3": "^0.20.0", "web3-provider-engine": "14.0.6"
"web3-provider-engine": "^14.0.4"
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"

View File

@ -1,11 +1,6 @@
import { logUtils } from '@0xproject/utils'; import { logUtils } from '@0xproject/utils';
import { uniqueVersionIds, Web3Wrapper } from '@0xproject/web3-wrapper'; import { NodeType, Web3Wrapper } from '@0xproject/web3-wrapper';
import { includes } from 'lodash'; import * as _ from 'lodash';
enum NodeType {
Geth = 'GETH',
Ganache = 'GANACHE',
}
// HACK(albrow): 🐉 We have to do this so that debug.setHead works correctly. // HACK(albrow): 🐉 We have to do this so that debug.setHead works correctly.
// (Geth does not seem to like debug.setHead(0), so by sending some transactions // (Geth does not seem to like debug.setHead(0), so by sending some transactions
@ -18,6 +13,7 @@ export class BlockchainLifecycle {
private _web3Wrapper: Web3Wrapper; private _web3Wrapper: Web3Wrapper;
private _snapshotIdsStack: number[]; private _snapshotIdsStack: number[];
private _addresses: string[] = []; private _addresses: string[] = [];
private _nodeType: NodeType | undefined;
constructor(web3Wrapper: Web3Wrapper) { constructor(web3Wrapper: Web3Wrapper) {
this._web3Wrapper = web3Wrapper; this._web3Wrapper = web3Wrapper;
this._snapshotIdsStack = []; this._snapshotIdsStack = [];
@ -61,16 +57,6 @@ export class BlockchainLifecycle {
throw new Error(`Unknown node type: ${nodeType}`); throw new Error(`Unknown node type: ${nodeType}`);
} }
} }
private async _getNodeTypeAsync(): Promise<NodeType> {
const version = await this._web3Wrapper.getNodeVersionAsync();
if (includes(version, uniqueVersionIds.geth)) {
return NodeType.Geth;
} else if (includes(version, uniqueVersionIds.ganache)) {
return NodeType.Ganache;
} else {
throw new Error(`Unknown client version: ${version}`);
}
}
private async _mineMinimumBlocksAsync(): Promise<void> { private async _mineMinimumBlocksAsync(): Promise<void> {
logUtils.warn('WARNING: minimum block number for tests not met. Mining additional blocks...'); logUtils.warn('WARNING: minimum block number for tests not met. Mining additional blocks...');
if (this._addresses.length === 0) { if (this._addresses.length === 0) {
@ -92,4 +78,10 @@ export class BlockchainLifecycle {
} }
logUtils.warn('Done mining the minimum number of blocks.'); logUtils.warn('Done mining the minimum number of blocks.');
} }
private async _getNodeTypeAsync(): Promise<NodeType> {
if (_.isUndefined(this._nodeType)) {
this._nodeType = await this._web3Wrapper.getNodeTypeAsync();
}
return this._nodeType;
}
} }

View File

@ -1,8 +1,3 @@
// HACK: web3 injects XMLHttpRequest into the global scope and ProviderEngine checks XMLHttpRequest
// to know whether it is running in a browser or node environment. We need it to be undefined since
// we are not running in a browser env.
// Filed issue: https://github.com/ethereum/web3.js/issues/844
(global as any).XMLHttpRequest = undefined;
import ProviderEngine = require('web3-provider-engine'); import ProviderEngine = require('web3-provider-engine');
import RpcSubprovider = require('web3-provider-engine/subproviders/rpc'); import RpcSubprovider = require('web3-provider-engine/subproviders/rpc');

View File

@ -5,6 +5,10 @@
{ {
"note": "Add `TraceParams` interface for `debug_traceTransaction` parameters", "note": "Add `TraceParams` interface for `debug_traceTransaction` parameters",
"pr": 675 "pr": 675
},
{
"note": "Add `TransactionReceiptStatus` type",
"pr": 812
} }
] ]
}, },

View File

@ -1,6 +1,6 @@
{ {
"name": "ethereum-types", "name": "ethereum-types",
"version": "0.0.1", "version": "0.0.2",
"engines": { "engines": {
"node": ">=6.12" "node": ">=6.12"
}, },

View File

@ -215,6 +215,8 @@ export interface TxDataPayable extends TxData {
value?: BigNumber; value?: BigNumber;
} }
export type TransactionReceiptStatus = null | string | 0 | 1;
export interface TransactionReceipt { export interface TransactionReceipt {
blockHash: string; blockHash: string;
blockNumber: number; blockNumber: number;
@ -222,7 +224,7 @@ export interface TransactionReceipt {
transactionIndex: number; transactionIndex: number;
from: string; from: string;
to: string; to: string;
status: null | string | 0 | 1; status: TransactionReceiptStatus;
cumulativeGasUsed: number; cumulativeGasUsed: number;
gasUsed: number; gasUsed: number;
contractAddress: string | null; contractAddress: string | null;

View File

@ -1,6 +1,6 @@
[ [
{ {
"version": "0.1.0", "version": "1.0.0",
"changes": [ "changes": [
{ {
"note": "Make fill-scenarios compatible with V2 of 0x protocol", "note": "Make fill-scenarios compatible with V2 of 0x protocol",

View File

@ -44,7 +44,7 @@
"@0xproject/base-contract": "^0.3.4", "@0xproject/base-contract": "^0.3.4",
"@0xproject/order-utils": "^1.0.0", "@0xproject/order-utils": "^1.0.0",
"@0xproject/types": "1.0.0", "@0xproject/types": "1.0.0",
"ethereum-types": "^0.0.1", "ethereum-types": "^0.0.2",
"@0xproject/typescript-typings": "^0.4.1", "@0xproject/typescript-typings": "^0.4.1",
"@0xproject/utils": "^0.7.1", "@0xproject/utils": "^0.7.1",
"@0xproject/web3-wrapper": "^0.7.1", "@0xproject/web3-wrapper": "^0.7.1",

View File

@ -1,4 +1,13 @@
[ [
{
"version": "1.0.0",
"changes": [
{
"note": "Update schemas for V2",
"pr": 615
}
]
},
{ {
"timestamp": 1529397769, "timestamp": 1529397769,
"version": "0.8.1", "version": "0.8.1",

View File

@ -45,7 +45,7 @@
"ethers": "3.0.22", "ethers": "3.0.22",
"lodash": "^4.17.4", "lodash": "^4.17.4",
"run-s": "^0.0.0", "run-s": "^0.0.0",
"web3-provider-engine": "^14.0.4" "web3-provider-engine": "14.0.6"
}, },
"devDependencies": { "devDependencies": {
"@0xproject/dev-utils": "^0.4.4", "@0xproject/dev-utils": "^0.4.4",

File diff suppressed because one or more lines are too long

View File

@ -67,10 +67,10 @@
"@0xproject/utils": "^0.7.1", "@0xproject/utils": "^0.7.1",
"@0xproject/web3-wrapper": "^0.7.1", "@0xproject/web3-wrapper": "^0.7.1",
"@ledgerhq/hw-app-eth": "^4.3.0", "@ledgerhq/hw-app-eth": "^4.3.0",
"ethereum-types": "^0.0.1", "ethereum-types": "^0.0.2",
"ethers": "3.0.22", "ethers": "3.0.22",
"lodash": "^4.17.4", "lodash": "^4.17.4",
"web3-provider-engine": "^14.0.4" "web3-provider-engine": "14.0.6"
}, },
"optionalDependencies": { "optionalDependencies": {
"@ledgerhq/hw-transport-node-hid": "^4.3.0" "@ledgerhq/hw-transport-node-hid": "^4.3.0"

View File

@ -1,4 +1,13 @@
[ [
{
"version": "0.2.2",
"changes": [
{
"note": "Fix git remote tag removal step & add an additional sanity assertion",
"pr": 803
}
]
},
{ {
"timestamp": 1529397769, "timestamp": 1529397769,
"version": "0.2.1", "version": "0.2.1",

View File

@ -34,17 +34,17 @@ async function checkGitTagsForNextVersionAndDeleteIfExistAsync(
const packageLocation = lernaPackage.location; const packageLocation = lernaPackage.location;
const nextVersion = await utils.getNextPackageVersionAsync(currentVersion, packageName, packageLocation); const nextVersion = await utils.getNextPackageVersionAsync(currentVersion, packageName, packageLocation);
const localTagVersions = localTagVersionsByPackageName[packageName];
if (_.includes(localTagVersions, nextVersion)) {
const tagName = `${packageName}@${nextVersion}`;
await utils.removeLocalTagAsync(tagName);
}
const remoteTagVersions = remoteTagVersionsByPackageName[packageName]; const remoteTagVersions = remoteTagVersionsByPackageName[packageName];
if (_.includes(remoteTagVersions, nextVersion)) { if (_.includes(remoteTagVersions, nextVersion)) {
const tagName = `:refs/tags/${packageName}@${nextVersion}`; const tagName = `:refs/tags/${packageName}@${nextVersion}`;
await utils.removeRemoteTagAsync(tagName); await utils.removeRemoteTagAsync(tagName);
} }
const localTagVersions = localTagVersionsByPackageName[packageName];
if (_.includes(localTagVersions, nextVersion)) {
const tagName = `${packageName}@${nextVersion}`;
await utils.removeLocalTagAsync(tagName);
}
} }
} }

View File

@ -44,6 +44,9 @@ export const utils = {
nextVersionIfValid = semver.inc(currentVersion, 'patch'); nextVersionIfValid = semver.inc(currentVersion, 'patch');
} }
const lastEntry = changelog[0]; const lastEntry = changelog[0];
if (semver.gt(currentVersion, lastEntry.version)) {
throw new Error(`Package.json version cannot be greater then last CHANGELOG entry. Check: ${packageName}`);
}
nextVersionIfValid = semver.eq(lastEntry.version, currentVersion) nextVersionIfValid = semver.eq(lastEntry.version, currentVersion)
? semver.inc(currentVersion, 'patch') ? semver.inc(currentVersion, 'patch')
: lastEntry.version; : lastEntry.version;
@ -100,19 +103,23 @@ export const utils = {
return tagVersionByPackageName; return tagVersionByPackageName;
}, },
async removeLocalTagAsync(tagName: string): Promise<void> { async removeLocalTagAsync(tagName: string): Promise<void> {
const result = await execAsync(`git tag -d ${tagName}`, { try {
cwd: constants.monorepoRootPath, await execAsync(`git tag -d ${tagName}`, {
}); cwd: constants.monorepoRootPath,
if (!_.isEmpty(result.stderr)) { });
throw new Error(`Failed to delete local git tag. Got err: ${result.stderr}`); } catch (err) {
throw new Error(`Failed to delete local git tag. Got err: ${err}`);
} }
this.log(`Removed local tag: ${tagName}`);
}, },
async removeRemoteTagAsync(tagName: string): Promise<void> { async removeRemoteTagAsync(tagName: string): Promise<void> {
const result = await execAsync(`git push origin ${tagName}`, { try {
cwd: constants.monorepoRootPath, await execAsync(`git push origin ${tagName}`, {
}); cwd: constants.monorepoRootPath,
if (!_.isEmpty(result.stderr)) { });
throw new Error(`Failed to delete remote git tag. Got err: ${result.stderr}`); } catch (err) {
throw new Error(`Failed to delete remote git tag. Got err: ${err}`);
} }
this.log(`Removed remote tag: ${tagName}`);
}, },
}; };

View File

@ -1,7 +1,11 @@
[ [
{ {
"version": "0.1.0", "version": "1.0.0",
"changes": [ "changes": [
{
"note": "Refactor to work with V2 of 0x protocol",
"pr": 636
},
{ {
"note": "Export parseECSignature method", "note": "Export parseECSignature method",
"pr": 684 "pr": 684

View File

@ -82,7 +82,7 @@
"@0xproject/web3-wrapper": "^0.7.1", "@0xproject/web3-wrapper": "^0.7.1",
"@types/node": "^8.0.53", "@types/node": "^8.0.53",
"bn.js": "^4.11.8", "bn.js": "^4.11.8",
"ethereum-types": "^0.0.1", "ethereum-types": "^0.0.2",
"ethereumjs-abi": "^0.6.4", "ethereumjs-abi": "^0.6.4",
"ethereumjs-util": "^5.1.1", "ethereumjs-util": "^5.1.1",
"ethers": "3.0.22", "ethers": "3.0.22",

View File

@ -85,9 +85,9 @@
"@0xproject/order-utils": "^0.0.7", "@0xproject/order-utils": "^0.0.7",
"@0xproject/types": "^0.8.1", "@0xproject/types": "^0.8.1",
"@0xproject/typescript-typings": "^0.4.1", "@0xproject/typescript-typings": "^0.4.1",
"@0xproject/utils": "^0.7.3", "@0xproject/utils": "^0.7.2",
"@0xproject/web3-wrapper": "^0.7.1", "@0xproject/web3-wrapper": "^0.7.1",
"ethereum-types": "^0.0.1", "ethereum-types": "^0.0.2",
"bintrees": "^1.0.2", "bintrees": "^1.0.2",
"ethers": "3.0.22", "ethers": "3.0.22",
"lodash": "^4.17.4" "lodash": "^4.17.4"

View File

@ -1,7 +1,10 @@
import { import {
BalanceAndProxyAllowanceLazyStore, BalanceAndProxyAllowanceLazyStore,
ContractWrappers, ContractWrappers,
LogCancelContractEventArgs,
LogFillContractEventArgs,
OrderFilledCancelledLazyStore, OrderFilledCancelledLazyStore,
WithdrawalContractEventArgs,
} from '@0xproject/contract-wrappers'; } from '@0xproject/contract-wrappers';
import { schemas } from '@0xproject/json-schemas'; import { schemas } from '@0xproject/json-schemas';
import { getOrderHashHex, OrderStateUtils } from '@0xproject/order-utils'; import { getOrderHashHex, OrderStateUtils } from '@0xproject/order-utils';
@ -23,14 +26,8 @@ import {
DepositContractEventArgs, DepositContractEventArgs,
EtherTokenContractEventArgs, EtherTokenContractEventArgs,
EtherTokenEvents, EtherTokenEvents,
WithdrawalContractEventArgs,
} from '../generated_contract_wrappers/ether_token'; } from '../generated_contract_wrappers/ether_token';
import { import { ExchangeContractEventArgs, ExchangeEvents } from '../generated_contract_wrappers/exchange';
ExchangeContractEventArgs,
ExchangeEvents,
LogCancelContractEventArgs,
LogFillContractEventArgs,
} from '../generated_contract_wrappers/exchange';
import { import {
ApprovalContractEventArgs, ApprovalContractEventArgs,
TokenContractEventArgs, TokenContractEventArgs,
@ -301,6 +298,7 @@ export class OrderWatcher {
} }
case EtherTokenEvents.Withdrawal: { case EtherTokenEvents.Withdrawal: {
// Invalidate cache // Invalidate cache
// tslint:disable-next-line:no-unnecessary-type-assertion
const args = decodedLog.args as WithdrawalContractEventArgs; const args = decodedLog.args as WithdrawalContractEventArgs;
this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._owner); this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._owner);
// Revalidate orders // Revalidate orders
@ -317,6 +315,7 @@ export class OrderWatcher {
} }
case ExchangeEvents.LogFill: { case ExchangeEvents.LogFill: {
// Invalidate cache // Invalidate cache
// tslint:disable-next-line:no-unnecessary-type-assertion
const args = decodedLog.args as LogFillContractEventArgs; const args = decodedLog.args as LogFillContractEventArgs;
this._orderFilledCancelledLazyStore.deleteFilledTakerAmount(args.orderHash); this._orderFilledCancelledLazyStore.deleteFilledTakerAmount(args.orderHash);
// Revalidate orders // Revalidate orders
@ -329,6 +328,7 @@ export class OrderWatcher {
} }
case ExchangeEvents.LogCancel: { case ExchangeEvents.LogCancel: {
// Invalidate cache // Invalidate cache
// tslint:disable-next-line:no-unnecessary-type-assertion
const args = decodedLog.args as LogCancelContractEventArgs; const args = decodedLog.args as LogCancelContractEventArgs;
this._orderFilledCancelledLazyStore.deleteCancelledTakerAmount(args.orderHash); this._orderFilledCancelledLazyStore.deleteCancelledTakerAmount(args.orderHash);
// Revalidate orders // Revalidate orders

View File

@ -84,7 +84,7 @@
"@0xproject/utils": "^0.7.1", "@0xproject/utils": "^0.7.1",
"@0xproject/web3-wrapper": "^0.7.1", "@0xproject/web3-wrapper": "^0.7.1",
"@types/yargs": "^11.0.0", "@types/yargs": "^11.0.0",
"ethereum-types": "^0.0.1", "ethereum-types": "^0.0.2",
"chalk": "^2.3.0", "chalk": "^2.3.0",
"ethereumjs-util": "^5.1.1", "ethereumjs-util": "^5.1.1",
"isomorphic-fetch": "^2.2.1", "isomorphic-fetch": "^2.2.1",
@ -93,7 +93,6 @@
"require-from-string": "^2.0.1", "require-from-string": "^2.0.1",
"semver": "^5.5.0", "semver": "^5.5.0",
"solc": "^0.4.23", "solc": "^0.4.23",
"web3": "^0.20.0",
"web3-eth-abi": "^1.0.0-beta.24", "web3-eth-abi": "^1.0.0-beta.24",
"yargs": "^10.0.3" "yargs": "^10.0.3"
}, },

View File

@ -56,7 +56,7 @@
"@0xproject/utils": "^0.7.1", "@0xproject/utils": "^0.7.1",
"@0xproject/web3-wrapper": "^0.7.1", "@0xproject/web3-wrapper": "^0.7.1",
"@0xproject/dev-utils": "^0.4.4", "@0xproject/dev-utils": "^0.4.4",
"ethereum-types": "^0.0.1", "ethereum-types": "^0.0.2",
"ethereumjs-util": "^5.1.1", "ethereumjs-util": "^5.1.1",
"glob": "^7.1.2", "glob": "^7.1.2",
"istanbul": "^0.4.5", "istanbul": "^0.4.5",

View File

@ -1,4 +1,12 @@
[ [
{
"version": "0.11.0",
"changes": [
{
"note": "Add `EthLightwalletSubprovider`"
}
]
},
{ {
"timestamp": 1529397769, "timestamp": 1529397769,
"version": "0.10.4", "version": "0.10.4",

View File

@ -41,27 +41,27 @@
}, },
"dependencies": { "dependencies": {
"@0xproject/assert": "^0.2.12", "@0xproject/assert": "^0.2.12",
"@0xproject/types": "^0.8.1", "@0xproject/types": "^1.0.0",
"@0xproject/typescript-typings": "^0.4.1", "@0xproject/web3-wrapper": "^0.7.1",
"@0xproject/typescript-typings": "^0.4.2",
"@0xproject/utils": "^0.7.1", "@0xproject/utils": "^0.7.1",
"@ledgerhq/hw-app-eth": "^4.3.0", "@ledgerhq/hw-app-eth": "^4.3.0",
"@ledgerhq/hw-transport-u2f": "^4.3.0", "@ledgerhq/hw-transport-u2f": "^4.3.0",
"ethereum-types": "^0.0.1", "ethereum-types": "^0.0.2",
"bip39": "^2.5.0", "bip39": "^2.5.0",
"bn.js": "^4.11.8", "bn.js": "^4.11.8",
"eth-lightwallet": "^3.0.1",
"ethereumjs-tx": "^1.3.5", "ethereumjs-tx": "^1.3.5",
"ethereumjs-util": "^5.1.1", "ethereumjs-util": "^5.1.1",
"ganache-core": "0xProject/ganache-core", "ganache-core": "0xProject/ganache-core",
"hdkey": "^0.7.1", "hdkey": "^0.7.1",
"lodash": "^4.17.4", "lodash": "^4.17.4",
"semaphore-async-await": "^1.5.1", "semaphore-async-await": "^1.5.1",
"web3": "^0.20.0", "web3-provider-engine": "14.0.6"
"web3-provider-engine": "^14.0.4"
}, },
"devDependencies": { "devDependencies": {
"@0xproject/monorepo-scripts": "^0.2.1", "@0xproject/monorepo-scripts": "^0.2.1",
"@0xproject/tslint-config": "^0.4.20", "@0xproject/tslint-config": "^0.4.20",
"@0xproject/typescript-typings": "^0.4.2",
"@0xproject/utils": "^0.7.1", "@0xproject/utils": "^0.7.1",
"@types/bip39": "^2.4.0", "@types/bip39": "^2.4.0",
"@types/bn.js": "^4.11.0", "@types/bn.js": "^4.11.0",
@ -70,6 +70,7 @@
"@types/lodash": "4.14.104", "@types/lodash": "4.14.104",
"@types/mocha": "^2.2.42", "@types/mocha": "^2.2.42",
"@types/node": "^8.0.53", "@types/node": "^8.0.53",
"@types/sinon": "^2.2.2",
"chai": "^4.0.1", "chai": "^4.0.1",
"chai-as-promised": "^7.1.0", "chai-as-promised": "^7.1.0",
"copyfiles": "^1.2.0", "copyfiles": "^1.2.0",
@ -79,6 +80,7 @@
"npm-run-all": "^4.1.2", "npm-run-all": "^4.1.2",
"nyc": "^11.0.1", "nyc": "^11.0.1",
"shx": "^0.2.2", "shx": "^0.2.2",
"sinon": "^4.0.0",
"tslint": "5.8.0", "tslint": "5.8.0",
"typedoc": "0xProject/typedoc", "typedoc": "0xProject/typedoc",
"typescript": "2.7.1", "typescript": "2.7.1",

View File

@ -1,8 +1,3 @@
// tslint:disable:max-classes-per-file
// tslint:disable:class-name
// tslint:disable:async-suffix
// tslint:disable:completed-docs
declare module '*.json' { declare module '*.json' {
const json: any; const json: any;
/* tslint:disable */ /* tslint:disable */

View File

@ -7,7 +7,7 @@ import { LedgerEthereumClient } from './types';
export { prependSubprovider } from './utils/subprovider_utils'; export { prependSubprovider } from './utils/subprovider_utils';
export { EmptyWalletSubprovider } from './subproviders/empty_wallet_subprovider'; export { EmptyWalletSubprovider } from './subproviders/empty_wallet_subprovider';
export { FakeGasEstimateSubprovider } from './subproviders/fake_gas_estimate_subprovider'; export { FakeGasEstimateSubprovider } from './subproviders/fake_gas_estimate_subprovider';
export { InjectedWeb3Subprovider } from './subproviders/injected_web3'; export { SignerSubprovider } from './subproviders/signer';
export { RedundantSubprovider } from './subproviders/redundant_subprovider'; export { RedundantSubprovider } from './subproviders/redundant_subprovider';
export { LedgerSubprovider } from './subproviders/ledger'; export { LedgerSubprovider } from './subproviders/ledger';
export { GanacheSubprovider } from './subproviders/ganache'; export { GanacheSubprovider } from './subproviders/ganache';
@ -15,6 +15,7 @@ export { Subprovider } from './subproviders/subprovider';
export { NonceTrackerSubprovider } from './subproviders/nonce_tracker'; export { NonceTrackerSubprovider } from './subproviders/nonce_tracker';
export { PrivateKeyWalletSubprovider } from './subproviders/private_key_wallet'; export { PrivateKeyWalletSubprovider } from './subproviders/private_key_wallet';
export { MnemonicWalletSubprovider } from './subproviders/mnemonic_wallet'; export { MnemonicWalletSubprovider } from './subproviders/mnemonic_wallet';
export { EthLightwalletSubprovider } from './subproviders/eth_lightwallet_subprovider';
export { export {
Callback, Callback,
ErrorCallback, ErrorCallback,

View File

@ -0,0 +1,68 @@
import * as lightwallet from 'eth-lightwallet';
import { PartialTxParams } from '../types';
import { BaseWalletSubprovider } from './base_wallet_subprovider';
import { PrivateKeyWalletSubprovider } from './private_key_wallet';
/*
* This class implements the web3-provider-engine subprovider interface and forwards
* requests involving user accounts and signing operations to eth-lightwallet
*
* Source: https://github.com/MetaMask/provider-engine/blob/master/subproviders/subprovider.js
*/
export class EthLightwalletSubprovider extends BaseWalletSubprovider {
private _keystore: lightwallet.keystore;
private _pwDerivedKey: Uint8Array;
constructor(keystore: lightwallet.keystore, pwDerivedKey: Uint8Array) {
super();
this._keystore = keystore;
this._pwDerivedKey = pwDerivedKey;
}
/**
* Retrieve the accounts associated with the eth-lightwallet instance.
* This method is implicitly called when issuing a `eth_accounts` JSON RPC request
* via your providerEngine instance.
*
* @return An array of accounts
*/
public async getAccountsAsync(): Promise<string[]> {
const accounts = this._keystore.getAddresses();
return accounts;
}
/**
* Signs a transaction with the account specificed by the `from` field in txParams.
* If you've added this Subprovider to your app's provider, you can simply send
* an `eth_sendTransaction` JSON RPC request, and this method will be called auto-magically.
* If you are not using this via a ProviderEngine instance, you can call it directly.
* @param txParams Parameters of the transaction to sign
* @return Signed transaction hex string
*/
public async signTransactionAsync(txParams: PartialTxParams): Promise<string> {
// Lightwallet loses the chain id information when hex encoding the transaction
// this results in a different signature on certain networks. PrivateKeyWallet
// respects this as it uses the parameters passed in
let privKey = this._keystore.exportPrivateKey(txParams.from, this._pwDerivedKey);
const privKeyWallet = new PrivateKeyWalletSubprovider(privKey);
privKey = '';
const privKeySignature = await privKeyWallet.signTransactionAsync(txParams);
return privKeySignature;
}
/**
* Sign a personal Ethereum signed message. The signing account will be the account
* associated with the provided address.
* If you've added the MnemonicWalletSubprovider to your app's provider, you can simply send an `eth_sign`
* or `personal_sign` JSON RPC request, and this method will be called auto-magically.
* If you are not using this via a ProviderEngine instance, you can call it directly.
* @param data Hex string message to sign
* @param address Address of the account to sign with
* @return Signature hex string (order: rsv)
*/
public async signPersonalMessageAsync(data: string, address: string): Promise<string> {
let privKey = this._keystore.exportPrivateKey(address, this._pwDerivedKey);
const privKeyWallet = new PrivateKeyWalletSubprovider(privKey);
privKey = '';
const result = privKeyWallet.signPersonalMessageAsync(data, address);
return result;
}
}

View File

@ -1,5 +1,5 @@
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import { JSONRPCRequestPayload, Provider } from 'ethereum-types'; import { JSONRPCRequestPayload, Provider } from 'ethereum-types';
import * as Web3 from 'web3';
import { Callback, ErrorCallback } from '../types'; import { Callback, ErrorCallback } from '../types';
@ -7,19 +7,19 @@ import { Subprovider } from './subprovider';
/** /**
* This class implements the [web3-provider-engine](https://github.com/MetaMask/provider-engine) * This class implements the [web3-provider-engine](https://github.com/MetaMask/provider-engine)
* subprovider interface. It forwards JSON RPC requests involving user accounts (getAccounts, * subprovider interface. It forwards JSON RPC requests involving the domain of a signer (getAccounts,
* sendTransaction, etc...) to the provider instance supplied at instantiation. All other requests * sendTransaction, signMessage etc...) to the provider instance supplied at instantiation. All other requests
* are passed onwards for subsequent subproviders to handle. * are passed onwards for subsequent subproviders to handle.
*/ */
export class InjectedWeb3Subprovider extends Subprovider { export class SignerSubprovider extends Subprovider {
private _injectedWeb3: Web3; private _web3Wrapper: Web3Wrapper;
/** /**
* Instantiates a new InjectedWeb3Subprovider * Instantiates a new SignerSubprovider
* @param provider Web3 provider that should handle all user account related requests * @param provider Web3 provider that should handle all user account related requests
*/ */
constructor(provider: Provider) { constructor(provider: Provider) {
super(); super();
this._injectedWeb3 = new Web3(provider); this._web3Wrapper = new Web3Wrapper(provider);
} }
/** /**
* This method conforms to the web3-provider-engine interface. * This method conforms to the web3-provider-engine interface.
@ -33,22 +33,39 @@ export class InjectedWeb3Subprovider extends Subprovider {
public async handleRequest(payload: JSONRPCRequestPayload, next: Callback, end: ErrorCallback): Promise<void> { public async handleRequest(payload: JSONRPCRequestPayload, next: Callback, end: ErrorCallback): Promise<void> {
switch (payload.method) { switch (payload.method) {
case 'web3_clientVersion': case 'web3_clientVersion':
this._injectedWeb3.version.getNode(end); try {
const nodeVersion = await this._web3Wrapper.getNodeVersionAsync();
end(null, nodeVersion);
} catch (err) {
end(err);
}
return; return;
case 'eth_accounts': case 'eth_accounts':
this._injectedWeb3.eth.getAccounts(end); try {
const accounts = await this._web3Wrapper.getAvailableAddressesAsync();
end(null, accounts);
} catch (err) {
end(err);
}
return; return;
case 'eth_sendTransaction': case 'eth_sendTransaction':
const [txParams] = payload.params; const [txParams] = payload.params;
this._injectedWeb3.eth.sendTransaction(txParams, end); try {
const txHash = await this._web3Wrapper.sendTransactionAsync(txParams);
end(null, txHash);
} catch (err) {
end(err);
}
return; return;
case 'eth_sign': case 'eth_sign':
const [address, message] = payload.params; const [address, message] = payload.params;
this._injectedWeb3.eth.sign(address, message, end); try {
const signature = await this._web3Wrapper.signMessageAsync(address, message);
end(null, signature);
} catch (err) {
end(err);
}
return; return;
default: default:
next(); next();
return; return;

View File

@ -0,0 +1,167 @@
import * as chai from 'chai';
import * as lightwallet from 'eth-lightwallet';
import { JSONRPCResponsePayload } from 'ethereum-types';
import * as ethUtils from 'ethereumjs-util';
import Web3ProviderEngine = require('web3-provider-engine');
import { EthLightwalletSubprovider } from '../../src';
import { DoneCallback } from '../../src/types';
import { chaiSetup } from '../chai_setup';
import { fixtureData } from '../utils/fixture_data';
import { ganacheSubprovider } from '../utils/ganache_subprovider';
import { reportCallbackErrors } from '../utils/report_callback_errors';
chaiSetup.configure();
const expect = chai.expect;
const DEFAULT_NUM_ACCOUNTS = 10;
const PASSWORD = 'supersecretpassword99';
const SALT = 'kvODghzs7Ff1uqHyI0P3wI4Hso4w4iWT2e9qmrWz0y4';
describe('EthLightwalletSubprovider', () => {
let ethLightwalletSubprovider: EthLightwalletSubprovider;
before(async () => {
const options = {
password: PASSWORD,
seedPhrase: fixtureData.TEST_RPC_MNEMONIC,
salt: SALT,
hdPathString: fixtureData.TESTRPC_BASE_DERIVATION_PATH,
};
const createVaultAsync = async (vaultOptions: lightwallet.VaultOptions) => {
return new Promise<lightwallet.keystore>(resolve => {
lightwallet.keystore.createVault(vaultOptions, (err: Error, vaultKeystore) => {
if (err) {
throw new Error(`Failed to createVault: ${err}`);
}
resolve(vaultKeystore);
});
});
};
const deriveKeyFromPasswordAsync = async (vaultKeystore: lightwallet.keystore) => {
return new Promise<Uint8Array>(resolve => {
vaultKeystore.keyFromPassword(PASSWORD, (err: Error, passwordDerivedKey: Uint8Array) => {
if (err) {
throw new Error(`Failed to get key from password: ${err}`);
}
resolve(passwordDerivedKey);
});
});
};
const keystore: lightwallet.keystore = await createVaultAsync(options);
const pwDerivedKey: Uint8Array = await deriveKeyFromPasswordAsync(keystore);
// Generate 10 addresses
keystore.generateNewAddress(pwDerivedKey, DEFAULT_NUM_ACCOUNTS);
ethLightwalletSubprovider = new EthLightwalletSubprovider(keystore, pwDerivedKey);
});
describe('direct method calls', () => {
describe('success cases', () => {
it('returns a list of accounts', async () => {
const accounts = await ethLightwalletSubprovider.getAccountsAsync();
expect(accounts[0]).to.be.equal(fixtureData.TEST_RPC_ACCOUNT_0);
expect(accounts[1]).to.be.equal(fixtureData.TEST_RPC_ACCOUNT_1);
expect(accounts.length).to.be.equal(DEFAULT_NUM_ACCOUNTS);
});
it('signs a personal message hash', async () => {
const accounts = await ethLightwalletSubprovider.getAccountsAsync();
const signingAccount = accounts[0];
const data = ethUtils.bufferToHex(ethUtils.toBuffer(fixtureData.PERSONAL_MESSAGE_STRING));
const ecSignatureHex = await ethLightwalletSubprovider.signPersonalMessageAsync(data, signingAccount);
expect(ecSignatureHex).to.be.equal(fixtureData.PERSONAL_MESSAGE_SIGNED_RESULT);
});
it('signs a transaction', async () => {
const txHex = await ethLightwalletSubprovider.signTransactionAsync(fixtureData.TX_DATA);
expect(txHex).to.be.equal(fixtureData.TX_DATA_SIGNED_RESULT);
});
});
});
describe('calls through a provider', () => {
let provider: Web3ProviderEngine;
before(() => {
provider = new Web3ProviderEngine();
provider.addProvider(ethLightwalletSubprovider);
provider.addProvider(ganacheSubprovider);
provider.start();
});
describe('success cases', () => {
it('returns a list of accounts', (done: DoneCallback) => {
const payload = {
jsonrpc: '2.0',
method: 'eth_accounts',
params: [],
id: 1,
};
const callback = reportCallbackErrors(done)((err: Error, response: JSONRPCResponsePayload) => {
expect(err).to.be.a('null');
expect(response.result[0]).to.be.equal(fixtureData.TEST_RPC_ACCOUNT_0);
expect(response.result.length).to.be.equal(DEFAULT_NUM_ACCOUNTS);
done();
});
provider.sendAsync(payload, callback);
});
it('signs a personal message hash with eth_sign', (done: DoneCallback) => {
const data = ethUtils.bufferToHex(ethUtils.toBuffer(fixtureData.PERSONAL_MESSAGE_STRING));
const account = fixtureData.TEST_RPC_ACCOUNT_0;
const payload = {
jsonrpc: '2.0',
method: 'eth_sign',
params: [account, data],
id: 1,
};
const callback = reportCallbackErrors(done)((err: Error, response: JSONRPCResponsePayload) => {
expect(err).to.be.a('null');
expect(response.result).to.be.equal(fixtureData.PERSONAL_MESSAGE_SIGNED_RESULT);
done();
});
provider.sendAsync(payload, callback);
});
it('signs a transaction', (done: DoneCallback) => {
const payload = {
jsonrpc: '2.0',
method: 'eth_signTransaction',
params: [fixtureData.TX_DATA],
id: 1,
};
const callback = reportCallbackErrors(done)((err: Error, response: JSONRPCResponsePayload) => {
expect(err).to.be.a('null');
expect(response.result.raw).to.be.equal(fixtureData.TX_DATA_SIGNED_RESULT);
done();
});
provider.sendAsync(payload, callback);
});
});
describe('failure cases', () => {
it('should throw if `data` param not hex when calling eth_sign', (done: DoneCallback) => {
const nonHexMessage = 'hello world';
const payload = {
jsonrpc: '2.0',
method: 'eth_sign',
params: [fixtureData.TEST_RPC_ACCOUNT_0, nonHexMessage],
id: 1,
};
const callback = reportCallbackErrors(done)((err: Error, _response: JSONRPCResponsePayload) => {
expect(err).to.not.be.a('null');
expect(err.message).to.be.equal('Expected data to be of type HexString, encountered: hello world');
done();
});
provider.sendAsync(payload, callback);
});
it('should throw if `data` param not hex when calling personal_sign', (done: DoneCallback) => {
const nonHexMessage = 'hello world';
const payload = {
jsonrpc: '2.0',
method: 'personal_sign',
params: [nonHexMessage, fixtureData.TEST_RPC_ACCOUNT_0],
id: 1,
};
const callback = reportCallbackErrors(done)((err: Error, _response: JSONRPCResponsePayload) => {
expect(err).to.not.be.a('null');
expect(err.message).to.be.equal('Expected data to be of type HexString, encountered: hello world');
done();
});
provider.sendAsync(payload, callback);
});
});
});
});

View File

@ -60,6 +60,20 @@ describe('PrivateKeyWalletSubprovider', () => {
}); });
provider.sendAsync(payload, callback); provider.sendAsync(payload, callback);
}); });
it('signs a transaction', (done: DoneCallback) => {
const payload = {
jsonrpc: '2.0',
method: 'eth_signTransaction',
params: [fixtureData.TX_DATA],
id: 1,
};
const callback = reportCallbackErrors(done)((err: Error, response: JSONRPCResponsePayload) => {
expect(err).to.be.a('null');
expect(response.result.raw).to.be.equal(fixtureData.TX_DATA_SIGNED_RESULT);
done();
});
provider.sendAsync(payload, callback);
});
it('signs a personal message with eth_sign', (done: DoneCallback) => { it('signs a personal message with eth_sign', (done: DoneCallback) => {
const messageHex = ethUtils.bufferToHex(ethUtils.toBuffer(fixtureData.PERSONAL_MESSAGE_STRING)); const messageHex = ethUtils.bufferToHex(ethUtils.toBuffer(fixtureData.PERSONAL_MESSAGE_STRING));
const payload = { const payload = {

View File

@ -1,6 +1,7 @@
import { DoneCallback } from '@0xproject/types'; import { DoneCallback } from '@0xproject/types';
import * as chai from 'chai'; import * as chai from 'chai';
import { JSONRPCResponsePayload } from 'ethereum-types'; import { JSONRPCResponsePayload } from 'ethereum-types';
import * as Sinon from 'sinon';
import Web3ProviderEngine = require('web3-provider-engine'); import Web3ProviderEngine = require('web3-provider-engine');
import RpcSubprovider = require('web3-provider-engine/subproviders/rpc'); import RpcSubprovider = require('web3-provider-engine/subproviders/rpc');
@ -41,6 +42,9 @@ describe('RedundantSubprovider', () => {
const nonExistentSubprovider = new RpcSubprovider({ const nonExistentSubprovider = new RpcSubprovider({
rpcUrl: 'http://does-not-exist:3000', rpcUrl: 'http://does-not-exist:3000',
}); });
const handleRequestStub = Sinon.stub(nonExistentSubprovider, 'handleRequest').throws(
new Error('REQUEST_FAILED'),
);
const subproviders = [nonExistentSubprovider as Subprovider, ganacheSubprovider]; const subproviders = [nonExistentSubprovider as Subprovider, ganacheSubprovider];
const redundantSubprovider = new RedundantSubprovider(subproviders); const redundantSubprovider = new RedundantSubprovider(subproviders);
provider.addProvider(redundantSubprovider); provider.addProvider(redundantSubprovider);
@ -55,6 +59,7 @@ describe('RedundantSubprovider', () => {
const callback = reportCallbackErrors(done)((err: Error, response: JSONRPCResponsePayload) => { const callback = reportCallbackErrors(done)((err: Error, response: JSONRPCResponsePayload) => {
expect(err).to.be.a('null'); expect(err).to.be.a('null');
expect(response.result.length).to.be.equal(DEFAULT_NUM_ACCOUNTS); expect(response.result.length).to.be.equal(DEFAULT_NUM_ACCOUNTS);
handleRequestStub.restore();
done(); done();
}); });
provider.sendAsync(payload, callback); provider.sendAsync(payload, callback);

View File

@ -20,6 +20,7 @@
"dependencies": { "dependencies": {
"0x.js": "^0.38.0", "0x.js": "^0.38.0",
"@0xproject/subproviders": "^0.10.4", "@0xproject/subproviders": "^0.10.4",
"@0xproject/web3-wrapper": "^0.7.1",
"@0xproject/typescript-typings": "^0.4.1", "@0xproject/typescript-typings": "^0.4.1",
"@0xproject/utils": "^0.7.1", "@0xproject/utils": "^0.7.1",
"body-parser": "^1.17.1", "body-parser": "^1.17.1",
@ -28,15 +29,14 @@
"express": "^4.15.2", "express": "^4.15.2",
"lodash": "^4.17.4", "lodash": "^4.17.4",
"rollbar": "^0.6.5", "rollbar": "^0.6.5",
"web3": "^0.20.0", "web3-provider-engine": "14.0.6"
"web3-provider-engine": "^14.0.4"
}, },
"devDependencies": { "devDependencies": {
"@0xproject/tslint-config": "^0.4.20", "@0xproject/tslint-config": "^0.4.20",
"@types/body-parser": "^1.16.1", "@types/body-parser": "^1.16.1",
"@types/express": "^4.0.35", "@types/express": "^4.0.35",
"@types/lodash": "4.14.104", "@types/lodash": "4.14.104",
"ethereum-types": "^0.0.1", "ethereum-types": "^0.0.2",
"awesome-typescript-loader": "^3.1.3", "awesome-typescript-loader": "^3.1.3",
"gulp": "^3.9.1", "gulp": "^3.9.1",
"make-promises-safe": "^1.1.0", "make-promises-safe": "^1.1.0",

View File

@ -1,7 +1,7 @@
import { ZeroEx } from '0x.js'; import { ZeroEx } from '0x.js';
import { BigNumber, logUtils, promisify } from '@0xproject/utils'; import { BigNumber, logUtils } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as _ from 'lodash'; import * as _ from 'lodash';
import * as Web3 from 'web3';
import { configs } from './configs'; import { configs } from './configs';
@ -13,22 +13,21 @@ const DISPENSE_MAX_AMOUNT_ETHER = 2;
type AsyncTask = () => Promise<void>; type AsyncTask = () => Promise<void>;
export const dispenseAssetTasks = { export const dispenseAssetTasks = {
dispenseEtherTask(recipientAddress: string, web3: Web3): AsyncTask { dispenseEtherTask(recipientAddress: string, web3Wrapper: Web3Wrapper): AsyncTask {
return async () => { return async () => {
logUtils.log(`Processing ETH ${recipientAddress}`); logUtils.log(`Processing ETH ${recipientAddress}`);
const userBalance = await promisify<BigNumber>(web3.eth.getBalance)(recipientAddress); const userBalance = await web3Wrapper.getBalanceInWeiAsync(recipientAddress);
const maxAmountInWei = new BigNumber(web3.toWei(DISPENSE_MAX_AMOUNT_ETHER, 'ether')); const maxAmountInWei = Web3Wrapper.toWei(new BigNumber(DISPENSE_MAX_AMOUNT_ETHER));
if (userBalance.greaterThanOrEqualTo(maxAmountInWei)) { if (userBalance.greaterThanOrEqualTo(maxAmountInWei)) {
logUtils.log( logUtils.log(
`User exceeded ETH balance maximum (${maxAmountInWei}) ${recipientAddress} ${userBalance} `, `User exceeded ETH balance maximum (${maxAmountInWei}) ${recipientAddress} ${userBalance} `,
); );
return; return;
} }
const sendTransactionAsync = promisify(web3.eth.sendTransaction); const txHash = await web3Wrapper.sendTransactionAsync({
const txHash = await sendTransactionAsync({
from: configs.DISPENSER_ADDRESS, from: configs.DISPENSER_ADDRESS,
to: recipientAddress, to: recipientAddress,
value: web3.toWei(DISPENSE_AMOUNT_ETHER, 'ether'), value: Web3Wrapper.toWei(new BigNumber(DISPENSE_AMOUNT_ETHER)),
}); });
logUtils.log(`Sent ${DISPENSE_AMOUNT_ETHER} ETH to ${recipientAddress} tx: ${txHash}`); logUtils.log(`Sent ${DISPENSE_AMOUNT_ETHER} ETH to ${recipientAddress} tx: ${txHash}`);
}; };

View File

@ -1,15 +1,10 @@
import { Order, ZeroEx } from '0x.js'; import { Order, ZeroEx } from '0x.js';
import { BigNumber, logUtils } from '@0xproject/utils'; import { BigNumber, logUtils } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import { Provider } from 'ethereum-types'; import { Provider } from 'ethereum-types';
import * as express from 'express'; import * as express from 'express';
import * as _ from 'lodash'; import * as _ from 'lodash';
import * as Web3 from 'web3';
// HACK: web3 injects XMLHttpRequest into the global scope and ProviderEngine checks XMLHttpRequest
// to know whether it is running in a browser or node environment. We need it to be undefined since
// we are not running in a browser env.
// Filed issue: https://github.com/ethereum/web3.js/issues/844
(global as any).XMLHttpRequest = undefined;
import { NonceTrackerSubprovider, PrivateKeyWalletSubprovider } from '@0xproject/subproviders'; import { NonceTrackerSubprovider, PrivateKeyWalletSubprovider } from '@0xproject/subproviders';
import ProviderEngine = require('web3-provider-engine'); import ProviderEngine = require('web3-provider-engine');
import RpcSubprovider = require('web3-provider-engine/subproviders/rpc'); import RpcSubprovider = require('web3-provider-engine/subproviders/rpc');
@ -22,7 +17,7 @@ import { rpcUrls } from './rpc_urls';
interface NetworkConfig { interface NetworkConfig {
dispatchQueue: DispatchQueue; dispatchQueue: DispatchQueue;
web3: Web3; web3Wrapper: Web3Wrapper;
zeroEx: ZeroEx; zeroEx: ZeroEx;
} }
@ -58,15 +53,15 @@ export class Handler {
constructor() { constructor() {
_.forIn(rpcUrls, (rpcUrl: string, networkId: string) => { _.forIn(rpcUrls, (rpcUrl: string, networkId: string) => {
const providerObj = Handler._createProviderEngine(rpcUrl); const providerObj = Handler._createProviderEngine(rpcUrl);
const web3 = new Web3(providerObj); const web3Wrapper = new Web3Wrapper(providerObj);
const zeroExConfig = { const zeroExConfig = {
networkId: +networkId, networkId: +networkId,
}; };
const zeroEx = new ZeroEx(web3.currentProvider, zeroExConfig); const zeroEx = new ZeroEx(providerObj, zeroExConfig);
const dispatchQueue = new DispatchQueue(); const dispatchQueue = new DispatchQueue();
this._networkConfigByNetworkId[networkId] = { this._networkConfigByNetworkId[networkId] = {
dispatchQueue, dispatchQueue,
web3, web3Wrapper,
zeroEx, zeroEx,
}; };
}); });
@ -106,7 +101,7 @@ export class Handler {
let dispenserTask; let dispenserTask;
switch (requestedAssetType) { switch (requestedAssetType) {
case RequestedAssetType.ETH: case RequestedAssetType.ETH:
dispenserTask = dispenseAssetTasks.dispenseEtherTask(recipient, networkConfig.web3); dispenserTask = dispenseAssetTasks.dispenseEtherTask(recipient, networkConfig.web3Wrapper);
break; break;
case RequestedAssetType.WETH: case RequestedAssetType.WETH:
case RequestedAssetType.ZRX: case RequestedAssetType.ZRX:

View File

@ -1,4 +1,15 @@
[ [
{
"version": "1.0.0",
"changes": [
{
"notes": "Updated types for V2 of 0x protocol"
},
{
"note": "Add `ECSignatureBuffer`"
}
]
},
{ {
"timestamp": 1529397769, "timestamp": 1529397769,
"version": "0.8.1", "version": "0.8.1",

View File

@ -35,7 +35,7 @@
"dependencies": { "dependencies": {
"@types/node": "^8.0.53", "@types/node": "^8.0.53",
"bignumber.js": "~4.1.0", "bignumber.js": "~4.1.0",
"ethereum-types": "^0.0.1" "ethereum-types": "^0.0.2"
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"

View File

@ -48,6 +48,12 @@ export interface ECSignature {
s: string; s: string;
} }
export interface ECSignatureBuffer {
v: number;
r: Buffer;
s: Buffer;
}
/** /**
* Validator signature components * Validator signature components
*/ */

View File

@ -2,6 +2,10 @@
{ {
"version": "0.4.2", "version": "0.4.2",
"changes": [ "changes": [
{
"note": "Add types for `eth-lightwallet`",
"pr": 775
},
{ {
"note": "Improve 'web3-provider-engine' typings", "note": "Improve 'web3-provider-engine' typings",
"pr": 768 "pr": 768

View File

@ -25,7 +25,7 @@
}, },
"homepage": "https://github.com/0xProject/0x-monorepo/packages/typescript-typings#readme", "homepage": "https://github.com/0xProject/0x-monorepo/packages/typescript-typings#readme",
"dependencies": { "dependencies": {
"ethereum-types": "^0.0.1", "ethereum-types": "^0.0.2",
"bignumber.js": "~4.1.0" "bignumber.js": "~4.1.0"
}, },
"devDependencies": { "devDependencies": {

View File

@ -0,0 +1,50 @@
// eth-lightwallet declarations
declare module 'eth-lightwallet' {
import { ECSignatureBuffer } from '@0xproject/types';
// tslint:disable-next-line:class-name
export class signing {
public static signTx(
keystore: keystore,
pwDerivedKey: Uint8Array,
rawTx: string,
signingAddress: string,
): string;
public static signMsg(
keystore: keystore,
pwDerivedKey: Uint8Array,
rawMsg: string,
signingAddress: string,
): ECSignatureBuffer;
public static signMsgHash(
keystore: keystore,
pwDerivedKey: Uint8Array,
msgHash: string,
signingAddress: string,
): ECSignatureBuffer;
public static concatSig(signature: any): string;
}
// tslint:disable-next-line:class-name
export class keystore {
public static createVault(options: any, callback?: (error: Error, keystore: keystore) => void): keystore;
public static generateRandomSeed(): string;
public static isSeedValid(seed: string): boolean;
public static deserialize(keystore: string): keystore;
public serialize(): string;
public keyFromPassword(
password: string,
callback?: (error: Error, pwDerivedKey: Uint8Array) => void,
): Uint8Array;
public isDerivedKeyCorrect(pwDerivedKey: Uint8Array): boolean;
public generateNewAddress(pwDerivedKey: Uint8Array, numberOfAddresses: number): void;
public getSeed(pwDerivedKey: Uint8Array): string;
public exportPrivateKey(address: string, pwDerivedKey: Uint8Array): string;
public getAddresses(): string[];
}
interface VaultOptions {
password: string;
seedPhrase: string;
salt?: string;
hdPathString: string;
}
}

View File

@ -1,13 +1,4 @@
[ [
{
"version": "0.7.3",
"changes": [
{
"note": "Fixes uncaught Error in abi_decoder",
"pr": 763
}
]
},
{ {
"version": "0.7.2", "version": "0.7.2",
"changes": [ "changes": [
@ -17,6 +8,10 @@
{ {
"note": "Add logUtils.warn", "note": "Add logUtils.warn",
"pr": 589 "pr": 589
},
{
"note": "Fixes uncaught Error in abi_decoder",
"pr": 763
} }
] ]
}, },

View File

@ -1,6 +1,6 @@
{ {
"name": "@0xproject/utils", "name": "@0xproject/utils",
"version": "0.7.3", "version": "0.7.2",
"engines": { "engines": {
"node": ">=6.12" "node": ">=6.12"
}, },
@ -35,15 +35,14 @@
"typescript": "2.7.1" "typescript": "2.7.1"
}, },
"dependencies": { "dependencies": {
"ethereum-types": "^0.0.1", "ethereum-types": "^0.0.2",
"@0xproject/typescript-typings": "^0.4.2", "@0xproject/typescript-typings": "^0.4.2",
"@types/node": "^8.0.53", "@types/node": "^8.0.53",
"ethereumjs-util": "^5.1.1", "ethereumjs-util": "^5.1.1",
"bignumber.js": "~4.1.0", "bignumber.js": "~4.1.0",
"ethers": "3.0.22", "ethers": "3.0.22",
"js-sha3": "^0.7.0", "js-sha3": "^0.7.0",
"lodash": "^4.17.4", "lodash": "^4.17.4"
"web3": "^0.20.0"
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"

View File

@ -1,4 +1,14 @@
[ [
{
"timestamp": 1529397769,
"version": "0.7.2",
"changes": [
{
"note": "Add `getNodeTypeAsync` method",
"pr": 812
}
]
},
{ {
"timestamp": 1529397769, "timestamp": 1529397769,
"version": "0.7.1", "version": "0.7.1",

View File

@ -68,7 +68,7 @@
"@0xproject/typescript-typings": "^0.4.1", "@0xproject/typescript-typings": "^0.4.1",
"@0xproject/utils": "^0.7.1", "@0xproject/utils": "^0.7.1",
"bn.js": "^4.11.8", "bn.js": "^4.11.8",
"ethereum-types": "^0.0.1", "ethereum-types": "^0.0.2",
"ethereumjs-util": "^5.1.1", "ethereumjs-util": "^5.1.1",
"ethers": "3.0.22", "ethers": "3.0.22",
"lodash": "^4.17.4", "lodash": "^4.17.4",

View File

@ -1,2 +1,2 @@
export { Web3Wrapper, uniqueVersionIds } from './web3_wrapper'; export { Web3Wrapper, uniqueVersionIds, NodeType } from './web3_wrapper';
export { Web3WrapperErrors } from './types'; export { Web3WrapperErrors } from './types';

View File

@ -39,6 +39,12 @@ export const uniqueVersionIds = {
ganache: 'EthereumJS TestRPC', ganache: 'EthereumJS TestRPC',
}; };
// NodeType represents the type of the backing Ethereum node.
export enum NodeType {
Geth = 'GETH',
Ganache = 'GANACHE',
}
/** /**
* An alternative to the Web3.js library that provides a consistent, clean, promise-based interface. * An alternative to the Web3.js library that provides a consistent, clean, promise-based interface.
*/ */
@ -595,6 +601,20 @@ export class Web3Wrapper {
assert.isNumber('blockNumber', blockNumber); assert.isNumber('blockNumber', blockNumber);
await this._sendRawPayloadAsync<void>({ method: 'debug_setHead', params: [web3Utils.toHex(blockNumber)] }); await this._sendRawPayloadAsync<void>({ method: 'debug_setHead', params: [web3Utils.toHex(blockNumber)] });
} }
/**
* Returns either NodeType.Geth or NodeType.Ganache depending on the type of
* the backing Ethereum node. Throws for any other type of node.
*/
public async getNodeTypeAsync(): Promise<NodeType> {
const version = await this.getNodeVersionAsync();
if (_.includes(version, uniqueVersionIds.geth)) {
return NodeType.Geth;
} else if (_.includes(version, uniqueVersionIds.ganache)) {
return NodeType.Ganache;
} else {
throw new Error(`Unknown client version: ${version}`);
}
}
private async _sendRawPayloadAsync<A>(payload: Partial<JSONRPCRequestPayload>): Promise<A> { private async _sendRawPayloadAsync<A>(payload: Partial<JSONRPCRequestPayload>): Promise<A> {
const sendAsync = this._provider.sendAsync.bind(this._provider); const sendAsync = this._provider.sendAsync.bind(this._provider);
const payloadWithDefaults = { const payloadWithDefaults = {

View File

@ -38,6 +38,7 @@
"lodash": "^4.17.4", "lodash": "^4.17.4",
"material-ui": "^0.17.1", "material-ui": "^0.17.1",
"moment": "2.21.0", "moment": "2.21.0",
"numeral": "^2.0.6",
"polished": "^1.9.2", "polished": "^1.9.2",
"query-string": "^6.0.0", "query-string": "^6.0.0",
"react": "15.6.1", "react": "15.6.1",
@ -57,8 +58,7 @@
"styled-components": "^3.3.0", "styled-components": "^3.3.0",
"thenby": "^1.2.3", "thenby": "^1.2.3",
"truffle-contract": "2.0.1", "truffle-contract": "2.0.1",
"web3": "^0.20.0", "web3-provider-engine": "14.0.6",
"web3-provider-engine": "^14.0.4",
"whatwg-fetch": "^2.0.3", "whatwg-fetch": "^2.0.3",
"xml-js": "^1.6.4" "xml-js": "^1.6.4"
}, },
@ -71,6 +71,7 @@
"@types/lodash": "4.14.104", "@types/lodash": "4.14.104",
"@types/material-ui": "0.18.0", "@types/material-ui": "0.18.0",
"@types/node": "^8.0.53", "@types/node": "^8.0.53",
"@types/numeral": "^0.0.22",
"@types/query-string": "^5.1.0", "@types/query-string": "^5.1.0",
"@types/react": "16.3.13", "@types/react": "16.3.13",
"@types/react-copy-to-clipboard": "^4.2.0", "@types/react-copy-to-clipboard": "^4.2.0",

View File

@ -0,0 +1,3 @@
<svg width="26" height="32" viewBox="0 0 26 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.47619 0C3.79509 0 1.60489 2.21216 1.60489 4.92014V6.33135C0.717479 6.33135 -3.60127e-08 7.05602 -3.60127e-08 7.95232V14.379C-3.60127e-08 15.2753 0.717479 16 1.60489 16H11.3475C12.2349 16 12.9524 15.2753 12.9524 14.379V7.95232C12.9524 7.05602 12.2349 6.33135 11.3475 6.33135V4.92014C11.3475 2.21216 9.1573 0 6.47619 0ZM9.6482 6.33135H3.30418V4.92014C3.30418 3.16567 4.72026 1.71633 6.47619 1.71633C8.23213 1.71633 9.6482 3.16567 9.6482 4.92014V6.33135Z" transform="scale(2)" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 604 B

View File

@ -0,0 +1,3 @@
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.5 9C16.5 13.1421 13.1421 16.5 9 16.5C4.85791 16.5 1.5 13.1421 1.5 9C1.5 4.85791 4.85791 1.5 9 1.5C13.1421 1.5 16.5 4.85791 16.5 9ZM18 9C18 13.9706 13.9707 18 9 18C4.0293 18 0 13.9706 0 9C0 4.02942 4.0293 0 9 0C13.9707 0 18 4.02942 18 9ZM9.21973 5.7196C9.5127 5.42664 9.9873 5.42664 10.2803 5.7196L13.0806 8.51953C13.373 8.8125 13.373 9.28735 13.0806 9.5802L10.2803 12.3802C9.9873 12.6731 9.5127 12.6731 9.21973 12.3802C8.92676 12.0873 8.92676 11.6124 9.21973 11.3196L10.7393 9.7998H4.75C4.33594 9.7998 4 9.46399 4 9.0498C4 8.63562 4.33594 8.2998 4.75 8.2998H10.7393L9.21973 6.78015C8.92676 6.4873 8.92676 6.01245 9.21973 5.7196Z" transform="scale(2)" fill="#3289F1"/>
</svg>

After

Width:  |  Height:  |  Size: 824 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

View File

@ -70,7 +70,18 @@
})(document, 'script', 'twitter-wjs'); })(document, 'script', 'twitter-wjs');
</script> </script>
<!-- End Twitter SDK --> <!-- End Twitter SDK -->
<!-- Hotjar Tracking Code for https://0xproject.com/ -->
<script>
(function (h, o, t, j, a, r) {
h.hj = h.hj || function () { (h.hj.q = h.hj.q || []).push(arguments) };
h._hjSettings = { hjid: 935597, hjsv: 6 };
a = o.getElementsByTagName('head')[0];
r = o.createElement('script'); r.async = 1;
r.src = t + h._hjSettings.hjid + j + h._hjSettings.hjsv;
a.appendChild(r);
})(window, document, 'https://static.hotjar.com/c/hotjar-', '.js?sv=');
</script>
<!-- End Hotjar Tracking Code -->
<!-- Main --> <!-- Main -->
<script type="text/javascript" crossorigin="anonymous" src="/bundle.js" charset="utf-8"></script> <script type="text/javascript" crossorigin="anonymous" src="/bundle.js" charset="utf-8"></script>
</body> </body>

View File

@ -12,10 +12,10 @@ import {
import { isValidOrderHash, signOrderHashAsync } from '@0xproject/order-utils'; import { isValidOrderHash, signOrderHashAsync } from '@0xproject/order-utils';
import { EtherscanLinkSuffixes, utils as sharedUtils } from '@0xproject/react-shared'; import { EtherscanLinkSuffixes, utils as sharedUtils } from '@0xproject/react-shared';
import { import {
InjectedWeb3Subprovider,
ledgerEthereumBrowserClientFactoryAsync, ledgerEthereumBrowserClientFactoryAsync,
LedgerSubprovider, LedgerSubprovider,
RedundantSubprovider, RedundantSubprovider,
SignerSubprovider,
Subprovider, Subprovider,
} from '@0xproject/subproviders'; } from '@0xproject/subproviders';
import { import {
@ -46,6 +46,7 @@ import {
Fill, Fill,
InjectedProviderObservable, InjectedProviderObservable,
InjectedProviderUpdate, InjectedProviderUpdate,
InjectedWeb3,
Order as PortalOrder, Order as PortalOrder,
Providers, Providers,
ProviderType, ProviderType,
@ -59,7 +60,6 @@ import { configs } from 'ts/utils/configs';
import { constants } from 'ts/utils/constants'; import { constants } from 'ts/utils/constants';
import { errorReporter } from 'ts/utils/error_reporter'; import { errorReporter } from 'ts/utils/error_reporter';
import { utils } from 'ts/utils/utils'; import { utils } from 'ts/utils/utils';
import Web3 = require('web3');
import ProviderEngine = require('web3-provider-engine'); import ProviderEngine = require('web3-provider-engine');
import FilterSubprovider = require('web3-provider-engine/subproviders/filters'); import FilterSubprovider = require('web3-provider-engine/subproviders/filters');
import RpcSubprovider = require('web3-provider-engine/subproviders/rpc'); import RpcSubprovider = require('web3-provider-engine/subproviders/rpc');
@ -73,6 +73,8 @@ const providerToName: { [provider: string]: string } = {
[Providers.Metamask]: constants.PROVIDER_NAME_METAMASK, [Providers.Metamask]: constants.PROVIDER_NAME_METAMASK,
[Providers.Parity]: constants.PROVIDER_NAME_PARITY_SIGNER, [Providers.Parity]: constants.PROVIDER_NAME_PARITY_SIGNER,
[Providers.Mist]: constants.PROVIDER_NAME_MIST, [Providers.Mist]: constants.PROVIDER_NAME_MIST,
[Providers.Toshi]: constants.PROVIDER_NAME_TOSHI,
[Providers.Cipher]: constants.PROVIDER_NAME_CIPHER,
}; };
export class Blockchain { export class Blockchain {
@ -95,8 +97,19 @@ export class Blockchain {
} }
return providerNameIfExists; return providerNameIfExists;
} }
private static _getInjectedWeb3(): any { private static _getInjectedWeb3(): InjectedWeb3 {
return (window as any).web3; const injectedWeb3IfExists = (window as any).web3;
// Our core assumptions about the injected web3 object is that it has the following
// properties and methods.
if (
_.isUndefined(injectedWeb3IfExists) ||
_.isUndefined(injectedWeb3IfExists.version) ||
_.isUndefined(injectedWeb3IfExists.version.getNetwork) ||
_.isUndefined(injectedWeb3IfExists.currentProvider)
) {
return undefined;
}
return injectedWeb3IfExists;
} }
private static async _getInjectedWeb3ProviderNetworkIdIfExistsAsync(): Promise<number | undefined> { private static async _getInjectedWeb3ProviderNetworkIdIfExistsAsync(): Promise<number | undefined> {
// Hack: We need to know the networkId the injectedWeb3 is connected to (if it is defined) in // Hack: We need to know the networkId the injectedWeb3 is connected to (if it is defined) in
@ -117,7 +130,7 @@ export class Blockchain {
return networkIdIfExists; return networkIdIfExists;
} }
private static async _getProviderAsync( private static async _getProviderAsync(
injectedWeb3: Web3, injectedWeb3: InjectedWeb3,
networkIdIfExists: number, networkIdIfExists: number,
shouldUserLedgerProvider: boolean = false, shouldUserLedgerProvider: boolean = false,
): Promise<[Provider, LedgerSubprovider | undefined]> { ): Promise<[Provider, LedgerSubprovider | undefined]> {
@ -151,7 +164,7 @@ export class Blockchain {
// We catch all requests involving a users account and send it to the injectedWeb3 // We catch all requests involving a users account and send it to the injectedWeb3
// instance. All other requests go to the public hosted node. // instance. All other requests go to the public hosted node.
const provider = new ProviderEngine(); const provider = new ProviderEngine();
provider.addProvider(new InjectedWeb3Subprovider(injectedWeb3.currentProvider)); provider.addProvider(new SignerSubprovider(injectedWeb3.currentProvider));
provider.addProvider(new FilterSubprovider()); provider.addProvider(new FilterSubprovider());
const rpcSubproviders = _.map(publicNodeUrlsIfExistsForNetworkId, publicNodeUrl => { const rpcSubproviders = _.map(publicNodeUrlsIfExistsForNetworkId, publicNodeUrl => {
return new RpcSubprovider({ return new RpcSubprovider({
@ -832,10 +845,10 @@ export class Blockchain {
this._dispatcher.updateNetworkId(networkId); this._dispatcher.updateNetworkId(networkId);
await this._rehydrateStoreWithContractEventsAsync(); await this._rehydrateStoreWithContractEventsAsync();
} }
private _updateProviderName(injectedWeb3: Web3): void { private _updateProviderName(injectedWeb3IfExists: InjectedWeb3): void {
const doesInjectedWeb3Exist = !_.isUndefined(injectedWeb3); const doesInjectedWeb3Exist = !_.isUndefined(injectedWeb3IfExists);
const providerName = doesInjectedWeb3Exist const providerName = doesInjectedWeb3Exist
? Blockchain._getNameGivenProvider(injectedWeb3.currentProvider) ? Blockchain._getNameGivenProvider(injectedWeb3IfExists.currentProvider)
: constants.PROVIDER_NAME_PUBLIC; : constants.PROVIDER_NAME_PUBLIC;
this._dispatcher.updateInjectedProviderName(providerName); this._dispatcher.updateInjectedProviderName(providerName);
} }

View File

@ -10,6 +10,7 @@ export class BlockchainWatcher {
private _watchBalanceIntervalId: NodeJS.Timer; private _watchBalanceIntervalId: NodeJS.Timer;
private _prevUserEtherBalanceInWei?: BigNumber; private _prevUserEtherBalanceInWei?: BigNumber;
private _prevUserAddressIfExists: string; private _prevUserAddressIfExists: string;
private _prevNodeVersionIfExists: string;
constructor(dispatcher: Dispatcher, web3Wrapper: Web3Wrapper, shouldPollUserAddress: boolean) { constructor(dispatcher: Dispatcher, web3Wrapper: Web3Wrapper, shouldPollUserAddress: boolean) {
this._dispatcher = dispatcher; this._dispatcher = dispatcher;
this._shouldPollUserAddress = shouldPollUserAddress; this._shouldPollUserAddress = shouldPollUserAddress;
@ -43,11 +44,9 @@ export class BlockchainWatcher {
); );
} }
private async _updateBalanceAsync(): Promise<void> { private async _updateBalanceAsync(): Promise<void> {
let prevNodeVersion: string;
// Check for node version changes
const currentNodeVersion = await this._web3Wrapper.getNodeVersionAsync(); const currentNodeVersion = await this._web3Wrapper.getNodeVersionAsync();
if (currentNodeVersion !== prevNodeVersion) { if (this._prevNodeVersionIfExists !== currentNodeVersion) {
prevNodeVersion = currentNodeVersion; this._prevNodeVersionIfExists = currentNodeVersion;
this._dispatcher.updateNodeVersion(currentNodeVersion); this._dispatcher.updateNodeVersion(currentNodeVersion);
} }

View File

@ -103,7 +103,6 @@ export class EthWethConversionDialog extends React.Component<
shouldCheckAllowance={false} shouldCheckAllowance={false}
onChange={this._onValueChange.bind(this)} onChange={this._onValueChange.bind(this)}
amount={this.state.value} amount={this.state.value}
onVisitBalancesPageClick={this.props.onCancelled}
/> />
) : ( ) : (
<EthAmountInput <EthAmountInput
@ -112,7 +111,6 @@ export class EthWethConversionDialog extends React.Component<
onChange={this._onValueChange.bind(this)} onChange={this._onValueChange.bind(this)}
shouldCheckBalance={true} shouldCheckBalance={true}
shouldShowIncompleteErrs={this.state.shouldShowIncompleteErrs} shouldShowIncompleteErrs={this.state.shouldShowIncompleteErrs}
onVisitBalancesPageClick={this.props.onCancelled}
/> />
)} )}
<div className="pt1" style={{ fontSize: 12 }}> <div className="pt1" style={{ fontSize: 12 }}>

View File

@ -80,7 +80,6 @@ export class SendDialog extends React.Component<SendDialogProps, SendDialogState
shouldCheckAllowance={false} shouldCheckAllowance={false}
onChange={this._onValueChange.bind(this)} onChange={this._onValueChange.bind(this)}
amount={this.state.value} amount={this.state.value}
onVisitBalancesPageClick={this.props.onCancelled}
lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch} lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch}
/> />
</div> </div>

View File

@ -3,9 +3,8 @@ import { BigNumber } from '@0xproject/utils';
import * as _ from 'lodash'; import * as _ from 'lodash';
import TextField from 'material-ui/TextField'; import TextField from 'material-ui/TextField';
import * as React from 'react'; import * as React from 'react';
import { Link } from 'react-router-dom';
import { RequiredLabel } from 'ts/components/ui/required_label'; import { RequiredLabel } from 'ts/components/ui/required_label';
import { ValidatedBigNumberCallback, WebsitePaths } from 'ts/types'; import { ValidatedBigNumberCallback } from 'ts/types';
import { utils } from 'ts/utils/utils'; import { utils } from 'ts/utils/utils';
interface BalanceBoundedInputProps { interface BalanceBoundedInputProps {
@ -18,8 +17,6 @@ interface BalanceBoundedInputProps {
shouldShowIncompleteErrs?: boolean; shouldShowIncompleteErrs?: boolean;
shouldCheckBalance: boolean; shouldCheckBalance: boolean;
validate?: (amount: BigNumber) => React.ReactNode; validate?: (amount: BigNumber) => React.ReactNode;
onVisitBalancesPageClick?: () => void;
shouldHideVisitBalancesLink?: boolean;
isDisabled?: boolean; isDisabled?: boolean;
shouldShowErrs?: boolean; shouldShowErrs?: boolean;
shouldShowUnderline?: boolean; shouldShowUnderline?: boolean;
@ -35,7 +32,6 @@ interface BalanceBoundedInputState {
export class BalanceBoundedInput extends React.Component<BalanceBoundedInputProps, BalanceBoundedInputState> { export class BalanceBoundedInput extends React.Component<BalanceBoundedInputProps, BalanceBoundedInputState> {
public static defaultProps: Partial<BalanceBoundedInputProps> = { public static defaultProps: Partial<BalanceBoundedInputProps> = {
shouldShowIncompleteErrs: false, shouldShowIncompleteErrs: false,
shouldHideVisitBalancesLink: false,
isDisabled: false, isDisabled: false,
shouldShowErrs: true, shouldShowErrs: true,
hintText: 'amount', hintText: 'amount',
@ -124,38 +120,11 @@ export class BalanceBoundedInput extends React.Component<BalanceBoundedInputProp
return 'Cannot be zero'; return 'Cannot be zero';
} }
if (this.props.shouldCheckBalance && amount.gt(balance)) { if (this.props.shouldCheckBalance && amount.gt(balance)) {
return <span>Insufficient balance. {this._renderIncreaseBalanceLink()}</span>; return <span>Insufficient balance.</span>;
} }
const errMsg = _.isUndefined(this.props.validate) ? undefined : this.props.validate(amount); const errMsg = _.isUndefined(this.props.validate) ? undefined : this.props.validate(amount);
return errMsg; return errMsg;
} }
private _renderIncreaseBalanceLink(): React.ReactNode {
if (this.props.shouldHideVisitBalancesLink) {
return null;
}
const increaseBalanceText = 'Increase balance';
const linkStyle = {
cursor: 'pointer',
color: colors.darkestGrey,
textDecoration: 'underline',
display: 'inline',
};
if (_.isUndefined(this.props.onVisitBalancesPageClick)) {
return (
<Link to={`${WebsitePaths.Portal}/balances`} style={linkStyle}>
{increaseBalanceText}
</Link>
);
} else {
return (
<div onClick={this.props.onVisitBalancesPageClick} style={linkStyle}>
{increaseBalanceText}
</div>
);
}
}
private _setAmountState(amount: string, balance: BigNumber, callback: () => void = _.noop): void { private _setAmountState(amount: string, balance: BigNumber, callback: () => void = _.noop): void {
const errorMsg = this._validate(amount, balance); const errorMsg = this._validate(amount, balance);
this.props.onErrorMsgChange(errorMsg); this.props.onErrorMsgChange(errorMsg);

View File

@ -14,9 +14,7 @@ interface EthAmountInputProps {
onChange: ValidatedBigNumberCallback; onChange: ValidatedBigNumberCallback;
onErrorMsgChange?: (errorMsg: React.ReactNode) => void; onErrorMsgChange?: (errorMsg: React.ReactNode) => void;
shouldShowIncompleteErrs: boolean; shouldShowIncompleteErrs: boolean;
onVisitBalancesPageClick?: () => void;
shouldCheckBalance: boolean; shouldCheckBalance: boolean;
shouldHideVisitBalancesLink?: boolean;
shouldShowErrs?: boolean; shouldShowErrs?: boolean;
shouldShowUnderline?: boolean; shouldShowUnderline?: boolean;
style?: React.CSSProperties; style?: React.CSSProperties;
@ -46,8 +44,6 @@ export class EthAmountInput extends React.Component<EthAmountInputProps, EthAmou
onErrorMsgChange={this.props.onErrorMsgChange} onErrorMsgChange={this.props.onErrorMsgChange}
shouldCheckBalance={this.props.shouldCheckBalance} shouldCheckBalance={this.props.shouldCheckBalance}
shouldShowIncompleteErrs={this.props.shouldShowIncompleteErrs} shouldShowIncompleteErrs={this.props.shouldShowIncompleteErrs}
onVisitBalancesPageClick={this.props.onVisitBalancesPageClick}
shouldHideVisitBalancesLink={this.props.shouldHideVisitBalancesLink}
hintText={this.props.hintText} hintText={this.props.hintText}
shouldShowErrs={this.props.shouldShowErrs} shouldShowErrs={this.props.shouldShowErrs}
shouldShowUnderline={this.props.shouldShowUnderline} shouldShowUnderline={this.props.shouldShowUnderline}

View File

@ -21,7 +21,6 @@ interface TokenAmountInputProps {
shouldCheckAllowance: boolean; shouldCheckAllowance: boolean;
onChange: ValidatedBigNumberCallback; onChange: ValidatedBigNumberCallback;
onErrorMsgChange?: (errorMsg: React.ReactNode) => void; onErrorMsgChange?: (errorMsg: React.ReactNode) => void;
onVisitBalancesPageClick?: () => void;
lastForceTokenStateRefetch: number; lastForceTokenStateRefetch: number;
shouldShowErrs?: boolean; shouldShowErrs?: boolean;
shouldShowUnderline?: boolean; shouldShowUnderline?: boolean;
@ -88,7 +87,6 @@ export class TokenAmountInput extends React.Component<TokenAmountInputProps, Tok
validate={this._validate.bind(this)} validate={this._validate.bind(this)}
shouldCheckBalance={this.props.shouldCheckBalance} shouldCheckBalance={this.props.shouldCheckBalance}
shouldShowIncompleteErrs={this.props.shouldShowIncompleteErrs} shouldShowIncompleteErrs={this.props.shouldShowIncompleteErrs}
onVisitBalancesPageClick={this.props.onVisitBalancesPageClick}
isDisabled={!this.state.isBalanceAndAllowanceLoaded} isDisabled={!this.state.isBalanceAndAllowanceLoaded}
hintText={this.props.hintText} hintText={this.props.hintText}
shouldShowErrs={this.props.shouldShowErrs} shouldShowErrs={this.props.shouldShowErrs}

View File

@ -39,7 +39,7 @@ export const OnboardingCard: React.StatelessComponent<OnboardingCardProps> = ({
borderRadius, borderRadius,
}) => ( }) => (
<Island borderRadius={borderRadius}> <Island borderRadius={borderRadius}>
<Container paddingRight="30px" paddingLeft="30px" maxWidth={350} paddingTop="15px" paddingBottom="15px"> <Container paddingRight="30px" paddingLeft="30px" paddingTop="15px" paddingBottom="15px">
<div className="flex flex-column"> <div className="flex flex-column">
<div className="flex justify-between"> <div className="flex justify-between">
<Title>{title}</Title> <Title>{title}</Title>

View File

@ -6,13 +6,29 @@ import { ContinueButtonDisplay, OnboardingTooltip } from 'ts/components/onboardi
import { Animation } from 'ts/components/ui/animation'; import { Animation } from 'ts/components/ui/animation';
import { Container } from 'ts/components/ui/container'; import { Container } from 'ts/components/ui/container';
import { Overlay } from 'ts/components/ui/overlay'; import { Overlay } from 'ts/components/ui/overlay';
import { PointerDirection } from 'ts/components/ui/pointer';
import { zIndex } from 'ts/style/z_index'; import { zIndex } from 'ts/style/z_index';
export interface Step { export interface FixedPositionSettings {
type: 'fixed';
top?: string;
bottom?: string;
left?: string;
right?: string;
pointerDirection?: PointerDirection;
}
export interface TargetPositionSettings {
type: 'target';
target: string; target: string;
placement: Placement;
}
export interface Step {
// Provide either a CSS selector, or fixed position settings. Only applies to desktop.
position: TargetPositionSettings | FixedPositionSettings;
title?: string; title?: string;
content: React.ReactNode; content: React.ReactNode;
placement?: Placement;
shouldHideBackButton?: boolean; shouldHideBackButton?: boolean;
shouldHideNextButton?: boolean; shouldHideNextButton?: boolean;
continueButtonDisplay?: ContinueButtonDisplay; continueButtonDisplay?: ContinueButtonDisplay;
@ -40,18 +56,30 @@ export class OnboardingFlow extends React.Component<OnboardingFlowProps> {
return null; return null;
} }
let onboardingElement = null; let onboardingElement = null;
const currentStep = this._getCurrentStep();
if (this.props.isMobile) { if (this.props.isMobile) {
onboardingElement = <Animation type="easeUpFromBottom">{this._renderOnboardignCard()}</Animation>; onboardingElement = <Animation type="easeUpFromBottom">{this._renderOnboardingCard()}</Animation>;
} else { } else if (currentStep.position.type === 'target') {
const { placement, target } = currentStep.position;
onboardingElement = ( onboardingElement = (
<Popper <Popper referenceElement={document.querySelector(target)} placement={placement} positionFixed={true}>
referenceElement={this._getElementForStep()}
placement={this._getCurrentStep().placement}
positionFixed={true}
>
{this._renderPopperChildren.bind(this)} {this._renderPopperChildren.bind(this)}
</Popper> </Popper>
); );
} else if (currentStep.position.type === 'fixed') {
const { top, right, bottom, left, pointerDirection } = currentStep.position;
onboardingElement = (
<Container
position="fixed"
zIndex={zIndex.aboveOverlay}
top={top}
right={right}
bottom={bottom}
left={left}
>
{this._renderToolTip(pointerDirection)}
</Container>
);
} }
if (this.props.disableOverlay) { if (this.props.disableOverlay) {
return onboardingElement; return onboardingElement;
@ -63,9 +91,6 @@ export class OnboardingFlow extends React.Component<OnboardingFlowProps> {
</div> </div>
); );
} }
private _getElementForStep(): Element {
return document.querySelector(this._getCurrentStep().target);
}
private _renderPopperChildren(props: PopperChildrenProps): React.ReactNode { private _renderPopperChildren(props: PopperChildrenProps): React.ReactNode {
const customStyles = { zIndex: zIndex.aboveOverlay }; const customStyles = { zIndex: zIndex.aboveOverlay };
// On re-render, we want to re-center the popper. // On re-render, we want to re-center the popper.
@ -76,7 +101,7 @@ export class OnboardingFlow extends React.Component<OnboardingFlowProps> {
</div> </div>
); );
} }
private _renderToolTip(): React.ReactNode { private _renderToolTip(pointerDirection?: PointerDirection): React.ReactNode {
const { steps, stepIndex } = this.props; const { steps, stepIndex } = this.props;
const step = steps[stepIndex]; const step = steps[stepIndex];
const isLastStep = steps.length - 1 === stepIndex; const isLastStep = steps.length - 1 === stepIndex;
@ -94,12 +119,13 @@ export class OnboardingFlow extends React.Component<OnboardingFlowProps> {
continueButtonDisplay={step.continueButtonDisplay} continueButtonDisplay={step.continueButtonDisplay}
continueButtonText={step.continueButtonText} continueButtonText={step.continueButtonText}
onContinueButtonClick={step.onContinueButtonClick} onContinueButtonClick={step.onContinueButtonClick}
pointerDirection={pointerDirection}
/> />
</Container> </Container>
); );
} }
private _renderOnboardignCard(): React.ReactNode { private _renderOnboardingCard(): React.ReactNode {
const { steps, stepIndex } = this.props; const { steps, stepIndex } = this.props;
const step = steps[stepIndex]; const step = steps[stepIndex];
const isLastStep = steps.length - 1 === stepIndex; const isLastStep = steps.length - 1 === stepIndex;

View File

@ -9,7 +9,12 @@ import { AddEthOnboardingStep } from 'ts/components/onboarding/add_eth_onboardin
import { CongratsOnboardingStep } from 'ts/components/onboarding/congrats_onboarding_step'; import { CongratsOnboardingStep } from 'ts/components/onboarding/congrats_onboarding_step';
import { InstallWalletOnboardingStep } from 'ts/components/onboarding/install_wallet_onboarding_step'; import { InstallWalletOnboardingStep } from 'ts/components/onboarding/install_wallet_onboarding_step';
import { IntroOnboardingStep } from 'ts/components/onboarding/intro_onboarding_step'; import { IntroOnboardingStep } from 'ts/components/onboarding/intro_onboarding_step';
import { OnboardingFlow, Step } from 'ts/components/onboarding/onboarding_flow'; import {
FixedPositionSettings,
OnboardingFlow,
Step,
TargetPositionSettings,
} from 'ts/components/onboarding/onboarding_flow';
import { SetAllowancesOnboardingStep } from 'ts/components/onboarding/set_allowances_onboarding_step'; import { SetAllowancesOnboardingStep } from 'ts/components/onboarding/set_allowances_onboarding_step';
import { UnlockWalletOnboardingStep } from 'ts/components/onboarding/unlock_wallet_onboarding_step'; import { UnlockWalletOnboardingStep } from 'ts/components/onboarding/unlock_wallet_onboarding_step';
import { import {
@ -45,8 +50,6 @@ class PlainPortalOnboardingFlow extends React.Component<PortalOnboardingFlowProp
private _unlisten: () => void; private _unlisten: () => void;
public componentDidMount(): void { public componentDidMount(): void {
this._adjustStepIfShould(); this._adjustStepIfShould();
// Wait until the step is adjusted to decide whether we should show onboarding.
setTimeout(this._autoStartOnboardingIfShould.bind(this), 1000);
// If there is a route change, just close onboarding. // If there is a route change, just close onboarding.
this._unlisten = this.props.history.listen(() => this.props.updateIsRunning(false)); this._unlisten = this.props.history.listen(() => this.props.updateIsRunning(false));
} }
@ -61,6 +64,9 @@ class PlainPortalOnboardingFlow extends React.Component<PortalOnboardingFlowProp
document.querySelector('.wallet').scrollIntoView(); document.querySelector('.wallet').scrollIntoView();
} }
} }
if (!prevProps.blockchainIsLoaded && this.props.blockchainIsLoaded) {
this._autoStartOnboardingIfShould();
}
} }
public render(): React.ReactNode { public render(): React.ReactNode {
return ( return (
@ -76,56 +82,61 @@ class PlainPortalOnboardingFlow extends React.Component<PortalOnboardingFlowProp
); );
} }
private _getSteps(): Step[] { private _getSteps(): Step[] {
const nextToWalletPosition: TargetPositionSettings = {
type: 'target',
target: '.wallet',
placement: 'right',
};
const underMetamaskExtension: FixedPositionSettings = {
type: 'fixed',
top: '30px',
right: '10px',
pointerDirection: 'top',
};
const steps: Step[] = [ const steps: Step[] = [
{ {
target: '.wallet', position: nextToWalletPosition,
title: '0x Ecosystem Setup', title: '0x Ecosystem Setup',
content: <InstallWalletOnboardingStep />, content: <InstallWalletOnboardingStep />,
placement: 'right',
shouldHideBackButton: true, shouldHideBackButton: true,
shouldHideNextButton: true, shouldHideNextButton: true,
}, },
{ {
target: '.wallet', position: underMetamaskExtension,
title: '0x Ecosystem Setup', title: '0x Ecosystem Setup',
content: <UnlockWalletOnboardingStep />, content: <UnlockWalletOnboardingStep />,
placement: 'right',
shouldHideBackButton: true, shouldHideBackButton: true,
shouldHideNextButton: true, shouldHideNextButton: true,
}, },
{ {
target: '.wallet', position: nextToWalletPosition,
title: '0x Ecosystem Account Setup', title: '0x Ecosystem Account Setup',
content: <IntroOnboardingStep />, content: <IntroOnboardingStep />,
placement: 'right',
shouldHideBackButton: true, shouldHideBackButton: true,
continueButtonDisplay: 'enabled', continueButtonDisplay: 'enabled',
}, },
{ {
target: '.wallet', position: nextToWalletPosition,
title: 'Step 1: Add ETH', title: 'Step 1: Add ETH',
content: ( content: (
<AddEthOnboardingStep userEthBalanceInWei={this.props.userEtherBalanceInWei || new BigNumber(0)} /> <AddEthOnboardingStep userEthBalanceInWei={this.props.userEtherBalanceInWei || new BigNumber(0)} />
), ),
placement: 'right',
continueButtonDisplay: this._userHasVisibleEth() ? 'enabled' : 'disabled', continueButtonDisplay: this._userHasVisibleEth() ? 'enabled' : 'disabled',
}, },
{ {
target: '.wallet', position: nextToWalletPosition,
title: 'Step 2: Wrap ETH', title: 'Step 2: Wrap ETH',
content: <WrapEthOnboardingStep1 />, content: <WrapEthOnboardingStep1 />,
placement: 'right',
continueButtonDisplay: 'enabled', continueButtonDisplay: 'enabled',
}, },
{ {
target: '.wallet', position: nextToWalletPosition,
title: 'Step 2: Wrap ETH', title: 'Step 2: Wrap ETH',
content: <WrapEthOnboardingStep2 />, content: <WrapEthOnboardingStep2 />,
placement: 'right',
continueButtonDisplay: this._userHasVisibleWeth() ? 'enabled' : 'disabled', continueButtonDisplay: this._userHasVisibleWeth() ? 'enabled' : 'disabled',
}, },
{ {
target: '.wallet', position: nextToWalletPosition,
title: 'Step 2: Wrap ETH', title: 'Step 2: Wrap ETH',
content: ( content: (
<WrapEthOnboardingStep3 <WrapEthOnboardingStep3
@ -134,11 +145,10 @@ class PlainPortalOnboardingFlow extends React.Component<PortalOnboardingFlowProp
} }
/> />
), ),
placement: 'right',
continueButtonDisplay: this._userHasVisibleWeth() ? 'enabled' : 'disabled', continueButtonDisplay: this._userHasVisibleWeth() ? 'enabled' : 'disabled',
}, },
{ {
target: '.wallet', position: nextToWalletPosition,
title: 'Step 3: Unlock Tokens', title: 'Step 3: Unlock Tokens',
content: ( content: (
<SetAllowancesOnboardingStep <SetAllowancesOnboardingStep
@ -147,14 +157,12 @@ class PlainPortalOnboardingFlow extends React.Component<PortalOnboardingFlowProp
doesUserHaveAllowancesForWethAndZrx={this._doesUserHaveAllowancesForWethAndZrx()} doesUserHaveAllowancesForWethAndZrx={this._doesUserHaveAllowancesForWethAndZrx()}
/> />
), ),
placement: 'right',
continueButtonDisplay: this._doesUserHaveAllowancesForWethAndZrx() ? 'enabled' : 'disabled', continueButtonDisplay: this._doesUserHaveAllowancesForWethAndZrx() ? 'enabled' : 'disabled',
}, },
{ {
target: '.wallet', position: nextToWalletPosition,
title: '🎉 The Ecosystem Awaits', title: '🎉 The Ecosystem Awaits',
content: <CongratsOnboardingStep />, content: <CongratsOnboardingStep />,
placement: 'right',
continueButtonDisplay: 'enabled', continueButtonDisplay: 'enabled',
shouldHideNextButton: true, shouldHideNextButton: true,
continueButtonText: 'Enter the 0x Ecosystem', continueButtonText: 'Enter the 0x Ecosystem',
@ -221,7 +229,7 @@ class PlainPortalOnboardingFlow extends React.Component<PortalOnboardingFlowProp
} }
private _autoStartOnboardingIfShould(): void { private _autoStartOnboardingIfShould(): void {
if ( if (
(this.props.stepIndex === 0 && !this.props.isRunning) || (this.props.stepIndex === 0 && !this.props.isRunning && this.props.blockchainIsLoaded) ||
(!this.props.isRunning && !this.props.hasBeenClosed && this.props.blockchainIsLoaded) (!this.props.isRunning && !this.props.hasBeenClosed && this.props.blockchainIsLoaded)
) { ) {
const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId]; const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId];

View File

@ -10,7 +10,7 @@ export const UnlockWalletOnboardingStep: React.StatelessComponent<UnlockWalletOn
<Container marginTop="15px" marginBottom="15px"> <Container marginTop="15px" marginBottom="15px">
<img src="/images/metamask_icon.png" height="50px" width="50px" /> <img src="/images/metamask_icon.png" height="50px" width="50px" />
</Container> </Container>
<Text center={true}>Unlock your metamask extension to get started.</Text> <Text center={true}>Unlock your MetaMask extension to get started.</Text>
</div> </div>
</div> </div>
); );

View File

@ -1,7 +1,6 @@
import { colors, constants as sharedConstants } from '@0xproject/react-shared'; import { colors, constants as sharedConstants } from '@0xproject/react-shared';
import { BigNumber } from '@0xproject/utils'; import { BigNumber } from '@0xproject/utils';
import * as _ from 'lodash'; import * as _ from 'lodash';
import Help from 'material-ui/svg-icons/action/help';
import * as React from 'react'; import * as React from 'react';
import * as DocumentTitle from 'react-document-title'; import * as DocumentTitle from 'react-document-title';
import { Link, Route, RouteComponentProps, Switch } from 'react-router-dom'; import { Link, Route, RouteComponentProps, Switch } from 'react-router-dom';
@ -24,6 +23,7 @@ import { TopBar, TopBarDisplayType } from 'ts/components/top_bar/top_bar';
import { TradeHistory } from 'ts/components/trade_history/trade_history'; import { TradeHistory } from 'ts/components/trade_history/trade_history';
import { Container } from 'ts/components/ui/container'; import { Container } from 'ts/components/ui/container';
import { FlashMessage } from 'ts/components/ui/flash_message'; import { FlashMessage } from 'ts/components/ui/flash_message';
import { Image } from 'ts/components/ui/image';
import { Text } from 'ts/components/ui/text'; import { Text } from 'ts/components/ui/text';
import { Wallet } from 'ts/components/wallet/wallet'; import { Wallet } from 'ts/components/wallet/wallet';
import { GenerateOrderForm } from 'ts/containers/generate_order_form'; import { GenerateOrderForm } from 'ts/containers/generate_order_form';
@ -368,11 +368,11 @@ export class Portal extends React.Component<PortalProps, PortalState> {
const shouldStartOnboarding = !isMobile || this.props.location.pathname === `${WebsitePaths.Portal}/account`; const shouldStartOnboarding = !isMobile || this.props.location.pathname === `${WebsitePaths.Portal}/account`;
const startOnboarding = ( const startOnboarding = (
<Container className="flex items-center center"> <Container className="flex items-center center">
<Help style={{ width: '20px', height: '20px' }} color={colors.mediumBlue} /> <Text fontColor={colors.mediumBlue} fontSize="16px" onClick={this._startOnboarding.bind(this)}>
<Container marginLeft="8px"> Set up your account to start trading
<Text fontColor={colors.mediumBlue} fontSize="16px" onClick={this._startOnboarding.bind(this)}> </Text>
Learn how to set up your account <Container marginLeft="8px" paddingTop="3px">
</Text> <Image src="/images/setup_account_icon.svg" height="20px" width="20x" />
</Container> </Container>
</Container> </Container>
); );

View File

@ -9,6 +9,7 @@ import { Container } from 'ts/components/ui/container';
import { Image } from 'ts/components/ui/image'; import { Image } from 'ts/components/ui/image';
import { Island } from 'ts/components/ui/island'; import { Island } from 'ts/components/ui/island';
import { colors } from 'ts/style/colors'; import { colors } from 'ts/style/colors';
import { media } from 'ts/style/media';
import { styled } from 'ts/style/theme'; import { styled } from 'ts/style/theme';
import { WebsiteBackendRelayerInfo } from 'ts/types'; import { WebsiteBackendRelayerInfo } from 'ts/types';
import { utils } from 'ts/utils/utils'; import { utils } from 'ts/utils/utils';
@ -55,7 +56,7 @@ const styles: Styles = {
}; };
const FALLBACK_IMG_SRC = '/images/relayer_fallback.png'; const FALLBACK_IMG_SRC = '/images/relayer_fallback.png';
const FALLBACK_PRIMARY_COLOR = colors.grey200; const FALLBACK_PRIMARY_COLOR = colors.grey300;
const NO_CONTENT_MESSAGE = '--'; const NO_CONTENT_MESSAGE = '--';
const RELAYER_ICON_HEIGHT = '110px'; const RELAYER_ICON_HEIGHT = '110px';
@ -111,6 +112,9 @@ const GridTile = styled(PlainGridTile)`
&:hover { &:hover {
transform: translate(0px, -3px); transform: translate(0px, -3px);
} }
${media.small`
transform: none;
`};
`; `;
interface SectionProps { interface SectionProps {

View File

@ -1,23 +1,26 @@
import { Styles } from '@0xproject/react-shared'; import { Styles } from '@0xproject/react-shared';
import * as _ from 'lodash'; import * as _ from 'lodash';
import CircularProgress from 'material-ui/CircularProgress'; import CircularProgress from 'material-ui/CircularProgress';
import RaisedButton from 'material-ui/RaisedButton';
import ActionAccountBalanceWallet from 'material-ui/svg-icons/action/account-balance-wallet'; import ActionAccountBalanceWallet from 'material-ui/svg-icons/action/account-balance-wallet';
import Lock from 'material-ui/svg-icons/action/lock';
import * as React from 'react'; import * as React from 'react';
import { Blockchain } from 'ts/blockchain'; import { Blockchain } from 'ts/blockchain';
import { ProviderPicker } from 'ts/components/top_bar/provider_picker';
import { AccountConnection } from 'ts/components/ui/account_connection'; import { AccountConnection } from 'ts/components/ui/account_connection';
import { Container } from 'ts/components/ui/container'; import { Container } from 'ts/components/ui/container';
import { DropDown } from 'ts/components/ui/drop_down'; import { DropDown } from 'ts/components/ui/drop_down';
import { Identicon } from 'ts/components/ui/identicon'; import { Identicon } from 'ts/components/ui/identicon';
import { Image } from 'ts/components/ui/image';
import { Island } from 'ts/components/ui/island'; import { Island } from 'ts/components/ui/island';
import {
CopyAddressSimpleMenuItem,
DifferentWalletSimpleMenuItem,
GoToAccountManagementSimpleMenuItem,
SimpleMenu,
} from 'ts/components/ui/simple_menu';
import { Text } from 'ts/components/ui/text'; import { Text } from 'ts/components/ui/text';
import { Dispatcher } from 'ts/redux/dispatcher'; import { Dispatcher } from 'ts/redux/dispatcher';
import { colors } from 'ts/style/colors'; import { colors } from 'ts/style/colors';
import { AccountState, ProviderType } from 'ts/types'; import { AccountState, ProviderType } from 'ts/types';
import { constants } from 'ts/utils/constants';
import { utils } from 'ts/utils/utils'; import { utils } from 'ts/utils/utils';
const ROOT_HEIGHT = 24; const ROOT_HEIGHT = 24;
@ -44,11 +47,7 @@ const styles: Styles = {
export class ProviderDisplay extends React.Component<ProviderDisplayProps, ProviderDisplayState> { export class ProviderDisplay extends React.Component<ProviderDisplayProps, ProviderDisplayState> {
public render(): React.ReactNode { public render(): React.ReactNode {
const isExternallyInjectedProvider = utils.isExternallyInjected( const activeNode = (
this.props.providerType,
this.props.injectedProviderName,
);
const hoverActiveNode = (
<Island className="flex items-center py1 px2" style={styles.root}> <Island className="flex items-center py1 px2" style={styles.root}>
{this._renderIcon()} {this._renderIcon()}
<Container marginLeft="12px" marginRight="12px"> <Container marginLeft="12px" marginRight="12px">
@ -57,93 +56,34 @@ export class ProviderDisplay extends React.Component<ProviderDisplayProps, Provi
{this._renderInjectedProvider()} {this._renderInjectedProvider()}
</Island> </Island>
); );
const hasLedgerProvider = this.props.providerType === ProviderType.Ledger;
const horizontalPosition = isExternallyInjectedProvider || hasLedgerProvider ? 'left' : 'middle';
return ( return (
<div style={{ width: 'fit-content', height: 48, float: 'right' }}> <div style={{ width: 'fit-content', height: 48, float: 'right' }}>
<DropDown <DropDown
hoverActiveNode={hoverActiveNode} activeNode={activeNode}
popoverContent={this.renderPopoverContent(isExternallyInjectedProvider, hasLedgerProvider)} popoverContent={this._renderPopoverContent()}
anchorOrigin={{ horizontal: horizontalPosition, vertical: 'bottom' }} anchorOrigin={{ horizontal: 'middle', vertical: 'bottom' }}
targetOrigin={{ horizontal: horizontalPosition, vertical: 'top' }} targetOrigin={{ horizontal: 'middle', vertical: 'top' }}
zDepth={1} zDepth={1}
/> />
</div> </div>
); );
} }
public renderPopoverContent(hasInjectedProvider: boolean, hasLedgerProvider: boolean): React.ReactNode { private _renderPopoverContent(): React.ReactNode {
if (!this._isBlockchainReady()) { const accountState = this._getAccountState();
return null; switch (accountState) {
} else if (hasInjectedProvider || hasLedgerProvider) { case AccountState.Ready:
return ( return (
<ProviderPicker <SimpleMenu>
dispatcher={this.props.dispatcher} <CopyAddressSimpleMenuItem userAddress={this.props.userAddress} />
networkId={this.props.networkId} <DifferentWalletSimpleMenuItem onClick={this.props.onToggleLedgerDialog} />
injectedProviderName={this.props.injectedProviderName} <GoToAccountManagementSimpleMenuItem />
providerType={this.props.providerType} </SimpleMenu>
onToggleLedgerDialog={this.props.onToggleLedgerDialog} );
blockchain={this.props.blockchain} case AccountState.Disconnected:
/> case AccountState.Locked:
); case AccountState.Loading:
} else { default:
// Nothing to connect to, show install/info popover return null;
return (
<div className="px2" style={{ maxWidth: 420 }}>
<div className="center h4 py2" style={{ color: colors.grey700 }}>
Choose a wallet:
</div>
<div className="flex pb3">
<div className="center px2">
<div style={{ color: colors.darkGrey }}>Install a browser wallet</div>
<div className="py2">
<img src="/images/metamask_or_parity.png" width="135" />
</div>
<div>
Use{' '}
<a
href={constants.URL_METAMASK_CHROME_STORE}
target="_blank"
style={{ color: colors.lightBlueA700 }}
>
Metamask
</a>{' '}
or{' '}
<a
href={constants.URL_PARITY_CHROME_STORE}
target="_blank"
style={{ color: colors.lightBlueA700 }}
>
Parity Signer
</a>
</div>
</div>
<div>
<div
className="pl1 ml1"
style={{ borderLeft: `1px solid ${colors.grey300}`, height: 65 }}
/>
<div className="py1">or</div>
<div
className="pl1 ml1"
style={{ borderLeft: `1px solid ${colors.grey300}`, height: 68 }}
/>
</div>
<div className="px2 center">
<div style={{ color: colors.darkGrey }}>Connect to a ledger hardware wallet</div>
<div style={{ paddingTop: 21, paddingBottom: 29 }}>
<img src="/images/ledger_icon.png" style={{ width: 80 }} />
</div>
<div>
<RaisedButton
style={{ width: '100%' }}
label="Use Ledger"
onClick={this.props.onToggleLedgerDialog}
/>
</div>
</div>
</div>
</div>
);
} }
} }
private _renderIcon(): React.ReactNode { private _renderIcon(): React.ReactNode {
@ -154,7 +94,7 @@ export class ProviderDisplay extends React.Component<ProviderDisplayProps, Provi
case AccountState.Loading: case AccountState.Loading:
return <CircularProgress size={ROOT_HEIGHT} thickness={2} />; return <CircularProgress size={ROOT_HEIGHT} thickness={2} />;
case AccountState.Locked: case AccountState.Locked:
return <Lock color={colors.black} />; return <Image src="/images/lock_icon.svg" height="20px" width="20px" />;
case AccountState.Disconnected: case AccountState.Disconnected:
return <ActionAccountBalanceWallet color={colors.mediumBlue} />; return <ActionAccountBalanceWallet color={colors.mediumBlue} />;
default: default:

View File

@ -1,79 +0,0 @@
import { colors, constants as sharedConstants } from '@0xproject/react-shared';
import { RadioButton, RadioButtonGroup } from 'material-ui/RadioButton';
import * as React from 'react';
import { Blockchain } from 'ts/blockchain';
import { Dispatcher } from 'ts/redux/dispatcher';
import { ProviderType } from 'ts/types';
interface ProviderPickerProps {
networkId: number;
injectedProviderName: string;
providerType: ProviderType;
onToggleLedgerDialog: () => void;
dispatcher: Dispatcher;
blockchain: Blockchain;
}
interface ProviderPickerState {}
export class ProviderPicker extends React.Component<ProviderPickerProps, ProviderPickerState> {
public render(): React.ReactNode {
const isLedgerSelected = this.props.providerType === ProviderType.Ledger;
const menuStyle = {
padding: 10,
paddingTop: 15,
paddingBottom: 15,
};
// Show dropdown with two options
return (
<div style={{ width: 225, overflow: 'hidden' }}>
<RadioButtonGroup name="provider" defaultSelected={this.props.providerType}>
<RadioButton
onClick={this._onProviderRadioChanged.bind(this, ProviderType.Injected)}
style={{ ...menuStyle, backgroundColor: !isLedgerSelected && colors.grey50 }}
value={ProviderType.Injected}
label={this._renderLabel(this.props.injectedProviderName, !isLedgerSelected)}
/>
<RadioButton
onClick={this._onProviderRadioChanged.bind(this, ProviderType.Ledger)}
style={{ ...menuStyle, backgroundColor: isLedgerSelected && colors.grey50 }}
value={ProviderType.Ledger}
label={this._renderLabel('Ledger Nano S', isLedgerSelected)}
/>
</RadioButtonGroup>
</div>
);
}
private _renderLabel(title: string, shouldShowNetwork: boolean): React.ReactNode {
const label = (
<div className="flex">
<div style={{ fontSize: 14 }}>{title}</div>
{shouldShowNetwork && this._renderNetwork()}
</div>
);
return label;
}
private _renderNetwork(): React.ReactNode {
const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId];
return (
<div className="flex" style={{ marginTop: 1 }}>
<div className="relative" style={{ width: 14, paddingLeft: 14 }}>
<img
src={`/images/network_icons/${networkName.toLowerCase()}.png`}
className="absolute"
style={{ top: 6, width: 10 }}
/>
</div>
<div style={{ color: colors.lightGrey, fontSize: 11 }}>{networkName}</div>
</div>
);
}
private _onProviderRadioChanged(value: string): void {
if (value === ProviderType.Ledger) {
this.props.onToggleLedgerDialog();
} else {
// tslint:disable-next-line:no-floating-promises
this.props.blockchain.updateProviderToInjectedAsync();
}
}
}

View File

@ -199,7 +199,7 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> {
cursor: 'pointer', cursor: 'pointer',
paddingTop: 16, paddingTop: 16,
}; };
const hoverActiveNode = ( const activeNode = (
<div className="flex relative" style={{ color: menuIconStyle.color }}> <div className="flex relative" style={{ color: menuIconStyle.color }}>
<div style={{ paddingRight: 10 }}>{this.props.translate.get(Key.Developers, Deco.Cap)}</div> <div style={{ paddingRight: 10 }}>{this.props.translate.get(Key.Developers, Deco.Cap)}</div>
<div className="absolute" style={{ paddingLeft: 3, right: 3, top: -2 }}> <div className="absolute" style={{ paddingLeft: 3, right: 3, top: -2 }}>
@ -224,7 +224,7 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> {
<div className={menuClasses}> <div className={menuClasses}>
<div className="flex justify-between"> <div className="flex justify-between">
<DropDown <DropDown
hoverActiveNode={hoverActiveNode} activeNode={activeNode}
popoverContent={popoverContent} popoverContent={popoverContent}
anchorOrigin={{ horizontal: 'middle', vertical: 'bottom' }} anchorOrigin={{ horizontal: 'middle', vertical: 'bottom' }}
targetOrigin={{ horizontal: 'middle', vertical: 'top' }} targetOrigin={{ horizontal: 'middle', vertical: 'top' }}

View File

@ -17,6 +17,7 @@ export interface ContainerProps {
maxHeight?: StringOrNum; maxHeight?: StringOrNum;
width?: StringOrNum; width?: StringOrNum;
height?: StringOrNum; height?: StringOrNum;
minWidth?: StringOrNum;
minHeight?: StringOrNum; minHeight?: StringOrNum;
isHidden?: boolean; isHidden?: boolean;
className?: string; className?: string;

View File

@ -1,4 +1,4 @@
import Popover, { PopoverAnimationVertical } from 'material-ui/Popover'; import Popover from 'material-ui/Popover';
import * as React from 'react'; import * as React from 'react';
import { MaterialUIPosition } from 'ts/types'; import { MaterialUIPosition } from 'ts/types';
@ -7,13 +7,20 @@ const DEFAULT_STYLE = {
fontSize: 14, fontSize: 14,
}; };
interface DropDownProps { export enum DropdownMouseEvent {
hoverActiveNode: React.ReactNode; Hover = 'hover',
Click = 'click',
}
export interface DropDownProps {
activeNode: React.ReactNode;
popoverContent: React.ReactNode; popoverContent: React.ReactNode;
anchorOrigin: MaterialUIPosition; anchorOrigin: MaterialUIPosition;
targetOrigin: MaterialUIPosition; targetOrigin: MaterialUIPosition;
style?: React.CSSProperties; style?: React.CSSProperties;
zDepth?: number; zDepth?: number;
activateEvent?: DropdownMouseEvent;
closeEvent?: DropdownMouseEvent;
} }
interface DropDownState { interface DropDownState {
@ -25,6 +32,8 @@ export class DropDown extends React.Component<DropDownProps, DropDownState> {
public static defaultProps: Partial<DropDownProps> = { public static defaultProps: Partial<DropDownProps> = {
style: DEFAULT_STYLE, style: DEFAULT_STYLE,
zDepth: 1, zDepth: 1,
activateEvent: DropdownMouseEvent.Hover,
closeEvent: DropdownMouseEvent.Hover,
}; };
private _isHovering: boolean; private _isHovering: boolean;
private _popoverCloseCheckIntervalId: number; private _popoverCloseCheckIntervalId: number;
@ -58,46 +67,61 @@ export class DropDown extends React.Component<DropDownProps, DropDownState> {
onMouseEnter={this._onHover.bind(this)} onMouseEnter={this._onHover.bind(this)}
onMouseLeave={this._onHoverOff.bind(this)} onMouseLeave={this._onHoverOff.bind(this)}
> >
{this.props.hoverActiveNode} <div onClick={this._onActiveNodeClick.bind(this)}>{this.props.activeNode}</div>
<Popover <Popover
open={this.state.isDropDownOpen} open={this.state.isDropDownOpen}
anchorEl={this.state.anchorEl} anchorEl={this.state.anchorEl}
anchorOrigin={this.props.anchorOrigin} anchorOrigin={this.props.anchorOrigin}
targetOrigin={this.props.targetOrigin} targetOrigin={this.props.targetOrigin}
onRequestClose={this._closePopover.bind(this)} onRequestClose={this._closePopover.bind(this)}
useLayerForClickAway={false} useLayerForClickAway={this.props.closeEvent === DropdownMouseEvent.Click}
animation={PopoverAnimationVertical} animated={false}
zDepth={this.props.zDepth} zDepth={this.props.zDepth}
> >
<div onMouseEnter={this._onHover.bind(this)} onMouseLeave={this._onHoverOff.bind(this)}> <div
onMouseEnter={this._onHover.bind(this)}
onMouseLeave={this._onHoverOff.bind(this)}
onClick={this._closePopover.bind(this)}
>
{this.props.popoverContent} {this.props.popoverContent}
</div> </div>
</Popover> </Popover>
</div> </div>
); );
} }
private _onActiveNodeClick(event: React.FormEvent<HTMLInputElement>): void {
if (this.props.activateEvent === DropdownMouseEvent.Click) {
this.setState({
isDropDownOpen: true,
anchorEl: event.currentTarget,
});
}
}
private _onHover(event: React.FormEvent<HTMLInputElement>): void { private _onHover(event: React.FormEvent<HTMLInputElement>): void {
this._isHovering = true; this._isHovering = true;
this._checkIfShouldOpenPopover(event); if (this.props.activateEvent === DropdownMouseEvent.Hover) {
this._checkIfShouldOpenPopover(event);
}
}
private _onHoverOff(): void {
this._isHovering = false;
} }
private _checkIfShouldOpenPopover(event: React.FormEvent<HTMLInputElement>): void { private _checkIfShouldOpenPopover(event: React.FormEvent<HTMLInputElement>): void {
if (this.state.isDropDownOpen) { if (this.state.isDropDownOpen) {
return; // noop return; // noop
} }
this.setState({ this.setState({
isDropDownOpen: true, isDropDownOpen: true,
anchorEl: event.currentTarget, anchorEl: event.currentTarget,
}); });
} }
private _onHoverOff(): void {
this._isHovering = false;
}
private _checkIfShouldClosePopover(): void { private _checkIfShouldClosePopover(): void {
if (!this.state.isDropDownOpen || this._isHovering) { if (!this.state.isDropDownOpen) {
return; // noop return; // noop
} }
this._closePopover(); if (this.props.closeEvent === DropdownMouseEvent.Hover && !this._isHovering) {
this._closePopover();
}
} }
private _closePopover(): void { private _closePopover(): void {
this.setState({ this.setState({

Some files were not shown because too many files have changed in this diff Show More