diff --git a/README.md b/README.md index 6615ae4dc8..1d6aa08a9a 100644 --- a/README.md +++ b/README.md @@ -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. +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/ [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) [![Coverage Status](https://coveralls.io/repos/github/0xProject/0x-monorepo/badge.svg?branch=development)](https://coveralls.io/github/0xProject/0x-monorepo?branch=development) diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json index 12fbf0347c..1b41a7cf23 100644 --- a/packages/0x.js/package.json +++ b/packages/0x.js/package.json @@ -110,7 +110,7 @@ "@0xproject/typescript-typings": "^0.4.1", "@0xproject/utils": "^0.7.1", "@0xproject/web3-wrapper": "^0.7.1", - "ethereum-types": "^0.0.1", + "ethereum-types": "^0.0.2", "ethers": "3.0.22", "lodash": "^4.17.4" }, diff --git a/packages/abi-gen/package.json b/packages/abi-gen/package.json index 9d3c6d00d2..dadf1dbfe2 100644 --- a/packages/abi-gen/package.json +++ b/packages/abi-gen/package.json @@ -29,7 +29,7 @@ "dependencies": { "@0xproject/typescript-typings": "^0.4.1", "@0xproject/utils": "^0.7.1", - "ethereum-types": "^0.0.1", + "ethereum-types": "^0.0.2", "chalk": "^2.3.0", "glob": "^7.1.2", "handlebars": "^4.0.11", diff --git a/packages/base-contract/package.json b/packages/base-contract/package.json index ecbde213bf..3b93830336 100644 --- a/packages/base-contract/package.json +++ b/packages/base-contract/package.json @@ -43,7 +43,7 @@ "typescript": "2.7.1" }, "dependencies": { - "ethereum-types": "^0.0.1", + "ethereum-types": "^0.0.2", "@0xproject/typescript-typings": "^0.4.1", "@0xproject/utils": "^0.7.1", "@0xproject/web3-wrapper": "^0.7.1", diff --git a/packages/contract-wrappers/CHANGELOG.json b/packages/contract-wrappers/CHANGELOG.json index 33d9bc4c0f..e34b50c1aa 100644 --- a/packages/contract-wrappers/CHANGELOG.json +++ b/packages/contract-wrappers/CHANGELOG.json @@ -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, "version": "0.0.5", diff --git a/packages/contract-wrappers/package.json b/packages/contract-wrappers/package.json index fe98e08bf0..373e023bd5 100644 --- a/packages/contract-wrappers/package.json +++ b/packages/contract-wrappers/package.json @@ -74,7 +74,7 @@ "source-map-support": "^0.5.0", "tslint": "5.8.0", "typescript": "2.7.1", - "web3-provider-engine": "^14.0.4" + "web3-provider-engine": "14.0.6" }, "dependencies": { "@0xproject/assert": "^0.2.12", @@ -86,8 +86,8 @@ "@0xproject/typescript-typings": "^0.4.1", "@0xproject/utils": "^0.7.1", "@0xproject/web3-wrapper": "^0.7.1", - "ethereum-types": "^0.0.1", - "ethereumjs-blockstream": "^2.0.6", + "ethereum-types": "^0.0.2", + "ethereumjs-blockstream": "5.0.0", "ethereumjs-util": "^5.1.1", "ethers": "3.0.22", "js-sha3": "^0.7.0", diff --git a/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts index 04f69bc3d0..9cc661080a 100644 --- a/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts @@ -9,7 +9,7 @@ import { } from '@0xproject/types'; import { intervalUtils } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; -import { Block, BlockAndLogStreamer } from 'ethereumjs-blockstream'; +import { Block, BlockAndLogStreamer, Log } from 'ethereumjs-blockstream'; import * as _ from 'lodash'; import { @@ -39,7 +39,7 @@ export abstract class ContractWrapper { public abstract abi: ContractAbi; protected _web3Wrapper: Web3Wrapper; protected _networkId: number; - private _blockAndLogStreamerIfExists?: BlockAndLogStreamer; + private _blockAndLogStreamerIfExists: BlockAndLogStreamer | undefined; private _blockAndLogStreamIntervalIfExists?: NodeJS.Timer; private _filters: { [filterToken: string]: FilterObject }; private _filterCallbacks: { @@ -163,6 +163,7 @@ export abstract class ContractWrapper { this._blockAndLogStreamerIfExists = new BlockAndLogStreamer( this._web3Wrapper.getBlockAsync.bind(this._web3Wrapper), this._web3Wrapper.getLogsAsync.bind(this._web3Wrapper), + this._onBlockAndLogStreamerError.bind(this), ); const catchAllLogFilter = {}; this._blockAndLogStreamerIfExists.addLogFilter(catchAllLogFilter); @@ -180,6 +181,14 @@ export abstract class ContractWrapper { 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 { const filterTokens = _.keys(this._filterCallbacks); _.each(filterTokens, filterToken => { diff --git a/packages/contract-wrappers/src/utils/decorators.ts b/packages/contract-wrappers/src/utils/decorators.ts index 494575e7ba..ccb4c6e11a 100644 --- a/packages/contract-wrappers/src/utils/decorators.ts +++ b/packages/contract-wrappers/src/utils/decorators.ts @@ -30,8 +30,8 @@ const schemaErrorTransformer = (error: Error) => { */ const asyncErrorHandlerFactory = (errorTransformer: ErrorTransformer) => { const asyncErrorHandlingDecorator = ( - target: object, - key: string | symbol, + _target: object, + _key: string | symbol, descriptor: TypedPropertyDescriptor, ) => { const originalMethod = descriptor.value as AsyncMethod; @@ -57,8 +57,8 @@ const asyncErrorHandlerFactory = (errorTransformer: ErrorTransformer) => { const syncErrorHandlerFactory = (errorTransformer: ErrorTransformer) => { const syncErrorHandlingDecorator = ( - target: object, - key: string | symbol, + _target: object, + _key: string | symbol, descriptor: TypedPropertyDescriptor, ) => { const originalMethod = descriptor.value as SyncMethod; diff --git a/packages/contract-wrappers/src/utils/exchange_transfer_simulator.ts b/packages/contract-wrappers/src/utils/exchange_transfer_simulator.ts index 527b8575dc..279f2a796b 100644 --- a/packages/contract-wrappers/src/utils/exchange_transfer_simulator.ts +++ b/packages/contract-wrappers/src/utils/exchange_transfer_simulator.ts @@ -1,8 +1,7 @@ -import { BlockParamLiteral, ExchangeContractErrs } from '@0xproject/types'; +import { ExchangeContractErrs } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { AbstractBalanceAndProxyAllowanceLazyStore } from '../abstract/abstract_balance_and_proxy_allowance_lazy_store'; -import { TokenWrapper } from '../contract_wrappers/token_wrapper'; import { TradeSide, TransferType } from '../types'; import { constants } from '../utils/constants'; diff --git a/packages/contract-wrappers/src/utils/filter_utils.ts b/packages/contract-wrappers/src/utils/filter_utils.ts index 5256d010f3..f96dc573f6 100644 --- a/packages/contract-wrappers/src/utils/filter_utils.ts +++ b/packages/contract-wrappers/src/utils/filter_utils.ts @@ -30,7 +30,7 @@ export const filterUtils = { blockRange?: BlockRange, ): FilterObject { 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 topicsForIndexedArgs = filterUtils.getTopicsForIndexedArgs(eventAbi, indexFilterValues); const topics = [topicForEventSignature, ...topicsForIndexedArgs]; @@ -46,7 +46,7 @@ export const filterUtils = { } return filter; }, - getEventSignatureFromAbiByName(eventAbi: EventAbi, eventName: ContractEvents): string { + getEventSignatureFromAbiByName(eventAbi: EventAbi): string { const types = _.map(eventAbi.inputs, 'type'); const signature = `${eventAbi.name}(${types.join(',')})`; return signature; diff --git a/packages/contract-wrappers/test/ether_token_wrapper_test.ts b/packages/contract-wrappers/test/ether_token_wrapper_test.ts index b13ac72bb7..f401ef90a6 100644 --- a/packages/contract-wrappers/test/ether_token_wrapper_test.ts +++ b/packages/contract-wrappers/test/ether_token_wrapper_test.ts @@ -277,7 +277,7 @@ describe('EtherTokenWrapper', () => { it('should cancel outstanding subscriptions when ZeroEx.setProvider is called', (done: DoneCallback) => { (async () => { const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)( - (logEvent: DecodedLogEvent) => { + (_logEvent: DecodedLogEvent) => { 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) => { (async () => { const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)( - (logEvent: DecodedLogEvent) => { + (_logEvent: DecodedLogEvent) => { done(new Error('Expected this subscription to have been cancelled')); }, ); diff --git a/packages/contract-wrappers/test/exchange_wrapper_test.ts b/packages/contract-wrappers/test/exchange_wrapper_test.ts index cf69d4813c..c945b4c70b 100644 --- a/packages/contract-wrappers/test/exchange_wrapper_test.ts +++ b/packages/contract-wrappers/test/exchange_wrapper_test.ts @@ -999,7 +999,7 @@ describe('ExchangeWrapper', () => { it('Outstanding subscriptions are cancelled when contractWrappers.setProvider called', (done: DoneCallback) => { (async () => { const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)( - (logEvent: DecodedLogEvent) => { + (_logEvent: DecodedLogEvent) => { 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) => { (async () => { const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)( - (logEvent: DecodedLogEvent) => { + (_logEvent: DecodedLogEvent) => { done(new Error('Expected this subscription to have been cancelled')); }, ); diff --git a/packages/contract-wrappers/test/subscription_test.ts b/packages/contract-wrappers/test/subscription_test.ts index 4d638bf9b8..b9417ca3d6 100644 --- a/packages/contract-wrappers/test/subscription_test.ts +++ b/packages/contract-wrappers/test/subscription_test.ts @@ -80,7 +80,7 @@ describe('SubscriptionTest', () => { }); it('Should allow unsubscribeAll to be called successfully after an error', (done: DoneCallback) => { (async () => { - const callback = (err: Error | null, logEvent?: DecodedLogEvent) => _.noop; + const callback = (_err: Error | null, _logEvent?: DecodedLogEvent) => _.noop; contractWrappers.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback); stubs = [ Sinon.stub((contractWrappers as any)._web3Wrapper, 'getBlockAsync').throws( diff --git a/packages/contract-wrappers/test/token_wrapper_test.ts b/packages/contract-wrappers/test/token_wrapper_test.ts index c9722c7b4b..86b93b3f9b 100644 --- a/packages/contract-wrappers/test/token_wrapper_test.ts +++ b/packages/contract-wrappers/test/token_wrapper_test.ts @@ -484,7 +484,7 @@ describe('TokenWrapper', () => { it('Outstanding subscriptions are cancelled when contractWrappers.setProvider called', (done: DoneCallback) => { (async () => { const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)( - (logEvent: DecodedLogEvent) => { + (_logEvent: DecodedLogEvent) => { 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) => { (async () => { const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)( - (logEvent: DecodedLogEvent) => { + (_logEvent: DecodedLogEvent) => { done(new Error('Expected this subscription to have been cancelled')); }, ); diff --git a/packages/contracts/package.json b/packages/contracts/package.json index e0e82adc7a..448871c238 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -80,12 +80,11 @@ "@0xproject/typescript-typings": "^0.4.1", "@0xproject/utils": "^0.7.1", "@0xproject/web3-wrapper": "^0.7.1", - "ethereum-types": "^0.0.1", + "ethereum-types": "^0.0.2", "bn.js": "^4.11.8", "ethereumjs-abi": "^0.6.4", "ethereumjs-util": "^5.1.1", "ethers": "3.0.22", - "lodash": "^4.17.4", - "web3": "^0.20.0" + "lodash": "^4.17.4" } } diff --git a/packages/contracts/test/asset_proxy/authorizable.ts b/packages/contracts/test/asset_proxy/authorizable.ts index 5a0586c286..e99c6cee34 100644 --- a/packages/contracts/test/asset_proxy/authorizable.ts +++ b/packages/contracts/test/asset_proxy/authorizable.ts @@ -5,7 +5,7 @@ import * as chai from 'chai'; import { MixinAuthorizableContract } from '../../generated_contract_wrappers/mixin_authorizable'; import { artifacts } from '../utils/artifacts'; -import { expectRevertReasonOrAlwaysFailingTransactionAsync } from '../utils/assertions'; +import { expectTransactionFailedAsync } from '../utils/assertions'; import { chaiSetup } from '../utils/chai_setup'; import { constants } from '../utils/constants'; import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper'; @@ -44,7 +44,7 @@ describe('Authorizable', () => { }); describe('addAuthorizedAddress', () => { it('should throw if not called by owner', async () => { - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( authorizable.addAuthorizedAddress.sendTransactionAsync(notOwner, { from: notOwner }), RevertReason.OnlyContractOwner, ); @@ -62,7 +62,7 @@ describe('Authorizable', () => { await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }), constants.AWAIT_TRANSACTION_MINED_MS, ); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }), RevertReason.TargetAlreadyAuthorized, ); @@ -75,7 +75,7 @@ describe('Authorizable', () => { await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }), constants.AWAIT_TRANSACTION_MINED_MS, ); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( authorizable.removeAuthorizedAddress.sendTransactionAsync(address, { from: notOwner, }), @@ -99,7 +99,7 @@ describe('Authorizable', () => { }); it('should throw if owner attempts to remove an address that is not authorized', async () => { - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( authorizable.removeAuthorizedAddress.sendTransactionAsync(address, { from: owner, }), @@ -115,7 +115,7 @@ describe('Authorizable', () => { constants.AWAIT_TRANSACTION_MINED_MS, ); const index = new BigNumber(0); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, { from: notOwner, }), @@ -128,7 +128,7 @@ describe('Authorizable', () => { constants.AWAIT_TRANSACTION_MINED_MS, ); const index = new BigNumber(1); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, { from: owner, }), @@ -137,7 +137,7 @@ describe('Authorizable', () => { }); it('should throw if owner attempts to remove an address that is not authorized', async () => { const index = new BigNumber(0); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, { from: owner, }), @@ -156,7 +156,7 @@ describe('Authorizable', () => { constants.AWAIT_TRANSACTION_MINED_MS, ); const address1Index = new BigNumber(0); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address2, address1Index, { from: owner, }), diff --git a/packages/contracts/test/asset_proxy/proxies.ts b/packages/contracts/test/asset_proxy/proxies.ts index fc1e53352b..bf9f9bc3eb 100644 --- a/packages/contracts/test/asset_proxy/proxies.ts +++ b/packages/contracts/test/asset_proxy/proxies.ts @@ -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 { IAssetProxyContract } from '../../generated_contract_wrappers/i_asset_proxy'; import { artifacts } from '../utils/artifacts'; -import { expectRevertReasonOrAlwaysFailingTransactionAsync } from '../utils/assertions'; +import { expectTransactionFailedAsync } from '../utils/assertions'; import { chaiSetup } from '../utils/chai_setup'; import { constants } from '../utils/constants'; import { ERC20Wrapper } from '../utils/erc20_wrapper'; @@ -184,7 +184,7 @@ describe('Asset Transfer Proxies', () => { constants.AWAIT_TRANSACTION_MINED_MS, ); // Perform a transfer; expect this to fail. - return expectRevertReasonOrAlwaysFailingTransactionAsync( + await expectTransactionFailedAsync( web3Wrapper.sendTransactionAsync({ to: erc20Proxy.address, data, @@ -205,7 +205,7 @@ describe('Asset Transfer Proxies', () => { takerAddress, amount, ); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + await expectTransactionFailedAsync( web3Wrapper.sendTransactionAsync({ to: erc20Proxy.address, data, @@ -344,7 +344,7 @@ describe('Asset Transfer Proxies', () => { erc20Proxy.address, // the ERC20 proxy does not have an ERC721 receiver amount, ); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( web3Wrapper.sendTransactionAsync({ to: erc721Proxy.address, data, @@ -369,7 +369,7 @@ describe('Asset Transfer Proxies', () => { takerAddress, amount, ); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( web3Wrapper.sendTransactionAsync({ to: erc721Proxy.address, data, @@ -393,7 +393,7 @@ describe('Asset Transfer Proxies', () => { takerAddress, amount, ); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( web3Wrapper.sendTransactionAsync({ to: erc721Proxy.address, data, @@ -421,7 +421,7 @@ describe('Asset Transfer Proxies', () => { takerAddress, amount, ); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( web3Wrapper.sendTransactionAsync({ to: erc721Proxy.address, data, @@ -442,7 +442,7 @@ describe('Asset Transfer Proxies', () => { takerAddress, amount, ); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( web3Wrapper.sendTransactionAsync({ to: erc721Proxy.address, data, diff --git a/packages/contracts/test/exchange/core.ts b/packages/contracts/test/exchange/core.ts index db56623f96..4e70893fc6 100644 --- a/packages/contracts/test/exchange/core.ts +++ b/packages/contracts/test/exchange/core.ts @@ -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 { CancelContractEventArgs, ExchangeContract } from '../../generated_contract_wrappers/exchange'; import { artifacts } from '../utils/artifacts'; -import { expectRevertReasonOrAlwaysFailingTransactionAsync } from '../utils/assertions'; +import { expectTransactionFailedAsync } from '../utils/assertions'; import { chaiSetup } from '../utils/chai_setup'; import { constants } from '../utils/constants'; import { ERC20Wrapper } from '../utils/erc20_wrapper'; @@ -144,7 +144,7 @@ describe('Exchange core', () => { const invalidSigBuff = Buffer.concat([v, invalidR, invalidS, signatureType]); const invalidSigHex = `0x${invalidSigBuff.toString('hex')}`; signedOrder.signature = invalidSigHex; - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), RevertReason.InvalidOrderSignature, ); @@ -153,7 +153,7 @@ describe('Exchange core', () => { it('should throw if no value is filled', async () => { signedOrder = orderFactory.newSignedOrder(); await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), RevertReason.OrderUnfillable, ); @@ -167,7 +167,7 @@ describe('Exchange core', () => { }); it('should throw if not sent by maker', async () => { - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.cancelOrderAsync(signedOrder, takerAddress), RevertReason.InvalidMaker, ); @@ -178,7 +178,7 @@ describe('Exchange core', () => { makerAssetAmount: new BigNumber(0), }); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress), RevertReason.OrderUnfillable, ); @@ -189,7 +189,7 @@ describe('Exchange core', () => { takerAssetAmount: new BigNumber(0), }); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress), RevertReason.OrderUnfillable, ); @@ -197,7 +197,7 @@ describe('Exchange core', () => { it('should be able to cancel a full order', async () => { await exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount: signedOrder.takerAssetAmount.div(2), }), @@ -222,7 +222,7 @@ describe('Exchange core', () => { it('should throw if already cancelled', async () => { await exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress), RevertReason.OrderUnfillable, ); @@ -232,7 +232,7 @@ describe('Exchange core', () => { signedOrder = orderFactory.newSignedOrder({ expirationTimeSeconds: new BigNumber(Math.floor((Date.now() - 10000) / 1000)), }); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress), RevertReason.OrderUnfillable, ); @@ -250,7 +250,7 @@ describe('Exchange core', () => { }); const fillTakerAssetAmount2 = new BigNumber(1); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount: fillTakerAssetAmount2, }), @@ -264,7 +264,7 @@ describe('Exchange core', () => { const orderEpoch = new BigNumber(1); await exchangeWrapper.cancelOrdersUpToAsync(orderEpoch, makerAddress); const lesserOrderEpoch = new BigNumber(0); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.cancelOrdersUpToAsync(lesserOrderEpoch, makerAddress), RevertReason.InvalidNewOrderEpoch, ); @@ -273,7 +273,7 @@ describe('Exchange core', () => { it('should fail to set orderEpoch equal to existing orderEpoch', async () => { const orderEpoch = new BigNumber(1); await exchangeWrapper.cancelOrdersUpToAsync(orderEpoch, makerAddress); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.cancelOrdersUpToAsync(orderEpoch, makerAddress), RevertReason.InvalidNewOrderEpoch, ); @@ -363,7 +363,7 @@ describe('Exchange core', () => { expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress); // Call Exchange const takerAssetFillAmount = signedOrder.takerAssetAmount; - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }), RevertReason.TransferFailed, ); @@ -386,7 +386,7 @@ describe('Exchange core', () => { expect(initialOwnerTakerAsset).to.be.bignumber.not.equal(takerAddress); // Call Exchange const takerAssetFillAmount = signedOrder.takerAssetAmount; - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }), RevertReason.TransferFailed, ); @@ -409,7 +409,7 @@ describe('Exchange core', () => { expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress); // Call Exchange const takerAssetFillAmount = signedOrder.takerAssetAmount; - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }), RevertReason.InvalidAmount, ); @@ -432,7 +432,7 @@ describe('Exchange core', () => { expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress); // Call Exchange const takerAssetFillAmount = signedOrder.takerAssetAmount; - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }), RevertReason.InvalidAmount, ); @@ -449,7 +449,7 @@ describe('Exchange core', () => { }); // Call Exchange const takerAssetFillAmount = signedOrder.takerAssetAmount.div(2); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }), RevertReason.RoundingError, ); @@ -475,7 +475,7 @@ describe('Exchange core', () => { expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress); // Call Exchange const takerAssetFillAmount = signedOrder.takerAssetAmount; - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }), RevertReason.LengthGreaterThan131Required, ); diff --git a/packages/contracts/test/exchange/dispatcher.ts b/packages/contracts/test/exchange/dispatcher.ts index afbf958d92..94ada1ef2b 100644 --- a/packages/contracts/test/exchange/dispatcher.ts +++ b/packages/contracts/test/exchange/dispatcher.ts @@ -14,7 +14,7 @@ import { TestAssetProxyDispatcherContract, } from '../../generated_contract_wrappers/test_asset_proxy_dispatcher'; import { artifacts } from '../utils/artifacts'; -import { expectRevertReasonOrAlwaysFailingTransactionAsync } from '../utils/assertions'; +import { expectTransactionFailedAsync } from '../utils/assertions'; import { chaiSetup } from '../utils/chai_setup'; import { constants } from '../utils/constants'; import { ERC20Wrapper } from '../utils/erc20_wrapper'; @@ -129,7 +129,7 @@ describe('AssetProxyDispatcher', () => { txDefaults, ); // Register new ERC20 Transfer Proxy contract - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(newErc20TransferProxy.address, { from: owner, }), @@ -138,7 +138,7 @@ describe('AssetProxyDispatcher', () => { }); it('should throw if requesting address is not owner', async () => { - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, { from: notOwner }), RevertReason.OnlyContractOwner, ); @@ -210,7 +210,7 @@ describe('AssetProxyDispatcher', () => { const encodedAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address); // Perform a transfer from makerAddress to takerAddress const amount = new BigNumber(10); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( assetProxyDispatcher.publicDispatchTransferFrom.sendTransactionAsync( encodedAssetData, makerAddress, diff --git a/packages/contracts/test/exchange/match_orders.ts b/packages/contracts/test/exchange/match_orders.ts index 0d07d156ff..90406415fe 100644 --- a/packages/contracts/test/exchange/match_orders.ts +++ b/packages/contracts/test/exchange/match_orders.ts @@ -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 { ExchangeContract } from '../../generated_contract_wrappers/exchange'; import { artifacts } from '../utils/artifacts'; -import { expectRevertReasonOrAlwaysFailingTransactionAsync } from '../utils/assertions'; +import { expectTransactionFailedAsync } from '../utils/assertions'; import { chaiSetup } from '../utils/chai_setup'; import { constants } from '../utils/constants'; import { ERC20Wrapper } from '../utils/erc20_wrapper'; @@ -602,7 +602,7 @@ describe('matchOrders', () => { // Cancel left order await exchangeWrapper.cancelOrderAsync(signedOrderLeft, signedOrderLeft.makerAddress); // Match orders - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress), RevertReason.OrderUnfillable, ); @@ -627,7 +627,7 @@ describe('matchOrders', () => { // Cancel right order await exchangeWrapper.cancelOrderAsync(signedOrderRight, signedOrderRight.makerAddress); // Match orders - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress), RevertReason.OrderUnfillable, ); @@ -650,7 +650,7 @@ describe('matchOrders', () => { feeRecipientAddress: feeRecipientAddressRight, }); // Match orders - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress), RevertReason.NegativeSpreadRequired, ); @@ -673,7 +673,7 @@ describe('matchOrders', () => { feeRecipientAddress: feeRecipientAddressRight, }); // Match orders - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress), // We are assuming assetData fields of the right order are the // reverse of the left order, rather than checking equality. This @@ -702,7 +702,7 @@ describe('matchOrders', () => { feeRecipientAddress: feeRecipientAddressRight, }); // Match orders - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress), RevertReason.InvalidOrderSignature, ); diff --git a/packages/contracts/test/exchange/signature_validator.ts b/packages/contracts/test/exchange/signature_validator.ts index 1db7dfc6d1..c44d22479c 100644 --- a/packages/contracts/test/exchange/signature_validator.ts +++ b/packages/contracts/test/exchange/signature_validator.ts @@ -13,7 +13,7 @@ import { TestValidatorContract } from '../../generated_contract_wrappers/test_va import { TestWalletContract } from '../../generated_contract_wrappers/test_wallet'; import { addressUtils } from '../utils/address_utils'; import { artifacts } from '../utils/artifacts'; -import { expectRevertOrOtherErrorAsync } from '../utils/assertions'; +import { expectContractCallFailed } from '../utils/assertions'; import { chaiSetup } from '../utils/chai_setup'; import { constants } from '../utils/constants'; import { LogDecoder } from '../utils/log_decoder'; @@ -101,7 +101,7 @@ describe('MixinSignatureValidator', () => { it('should revert when signature is empty', async () => { const emptySignature = '0x'; const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); - return expectRevertOrOtherErrorAsync( + return expectContractCallFailed( signatureValidator.publicIsValidSignature.callAsync( orderHashHex, signedOrder.makerAddress, @@ -115,7 +115,7 @@ describe('MixinSignatureValidator', () => { const unsupportedSignatureType = SignatureType.NSignatureTypes; const unsupportedSignatureHex = `0x${unsupportedSignatureType}`; const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); - return expectRevertOrOtherErrorAsync( + return expectContractCallFailed( signatureValidator.publicIsValidSignature.callAsync( orderHashHex, signedOrder.makerAddress, @@ -128,7 +128,7 @@ describe('MixinSignatureValidator', () => { it('should revert when SignatureType=Illegal', async () => { const unsupportedSignatureHex = `0x${SignatureType.Illegal}`; const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); - return expectRevertOrOtherErrorAsync( + return expectContractCallFailed( signatureValidator.publicIsValidSignature.callAsync( orderHashHex, signedOrder.makerAddress, @@ -155,7 +155,7 @@ describe('MixinSignatureValidator', () => { const signatureBuffer = Buffer.concat([fillerData, signatureType]); const signatureHex = ethUtil.bufferToHex(signatureBuffer); const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); - return expectRevertOrOtherErrorAsync( + return expectContractCallFailed( signatureValidator.publicIsValidSignature.callAsync( orderHashHex, signedOrder.makerAddress, diff --git a/packages/contracts/test/exchange/transactions.ts b/packages/contracts/test/exchange/transactions.ts index 4f8b49e0e5..959d795178 100644 --- a/packages/contracts/test/exchange/transactions.ts +++ b/packages/contracts/test/exchange/transactions.ts @@ -11,7 +11,7 @@ import { ExchangeContract } from '../../generated_contract_wrappers/exchange'; import { ExchangeWrapperContract } from '../../generated_contract_wrappers/exchange_wrapper'; import { WhitelistContract } from '../../generated_contract_wrappers/whitelist'; import { artifacts } from '../utils/artifacts'; -import { expectRevertReasonOrAlwaysFailingTransactionAsync } from '../utils/assertions'; +import { expectTransactionFailedAsync } from '../utils/assertions'; import { chaiSetup } from '../utils/chai_setup'; import { constants } from '../utils/constants'; import { ERC20Wrapper } from '../utils/erc20_wrapper'; @@ -134,7 +134,7 @@ describe('Exchange transactions', () => { }); it('should throw if not called by specified sender', async () => { - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.executeTransactionAsync(signedTx, takerAddress), 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 () => { await exchangeWrapper.executeTransactionAsync(signedTx, senderAddress); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.executeTransactionAsync(signedTx, senderAddress), RevertReason.InvalidTxHash, ); @@ -197,7 +197,7 @@ describe('Exchange transactions', () => { }); it('should throw if not called by specified sender', async () => { - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.executeTransactionAsync(signedTx, makerAddress), RevertReason.FailedExecution, ); @@ -205,7 +205,7 @@ describe('Exchange transactions', () => { it('should cancel the order when signed by maker and called by sender', async () => { await exchangeWrapper.executeTransactionAsync(signedTx, senderAddress); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.fillOrderAsync(signedOrder, senderAddress), RevertReason.OrderUnfillable, ); @@ -250,7 +250,7 @@ describe('Exchange transactions', () => { signedOrder.signature, ); const signedFillTx = takerTransactionFactory.newSignedTransaction(fillData); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapperContract.fillOrder.sendTransactionAsync( orderWithoutExchangeAddress, takerAssetFillAmount, @@ -370,7 +370,7 @@ describe('Exchange transactions', () => { orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder); const takerAssetFillAmount = signedOrder.takerAssetAmount; const salt = generatePseudoRandomSalt(); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( whitelist.fillOrderIfWhitelisted.sendTransactionAsync( orderWithoutExchangeAddress, takerAssetFillAmount, @@ -392,7 +392,7 @@ describe('Exchange transactions', () => { orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder); const takerAssetFillAmount = signedOrder.takerAssetAmount; const salt = generatePseudoRandomSalt(); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( whitelist.fillOrderIfWhitelisted.sendTransactionAsync( orderWithoutExchangeAddress, takerAssetFillAmount, diff --git a/packages/contracts/test/exchange/wrapper.ts b/packages/contracts/test/exchange/wrapper.ts index 7942f76951..69f374e461 100644 --- a/packages/contracts/test/exchange/wrapper.ts +++ b/packages/contracts/test/exchange/wrapper.ts @@ -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 { ExchangeContract } from '../../generated_contract_wrappers/exchange'; import { artifacts } from '../utils/artifacts'; -import { expectRevertReasonOrAlwaysFailingTransactionAsync } from '../utils/assertions'; +import { expectTransactionFailedAsync } from '../utils/assertions'; import { chaiSetup } from '../utils/chai_setup'; import { constants } from '../utils/constants'; import { ERC20Wrapper } from '../utils/erc20_wrapper'; @@ -174,7 +174,7 @@ describe('Exchange wrappers', () => { expirationTimeSeconds: new BigNumber(Math.floor((Date.now() - 10000) / 1000)), }); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.fillOrKillOrderAsync(signedOrder, takerAddress), RevertReason.OrderUnfillable, ); @@ -187,7 +187,7 @@ describe('Exchange wrappers', () => { takerAssetFillAmount: signedOrder.takerAssetAmount.div(2), }); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.fillOrKillOrderAsync(signedOrder, takerAddress), RevertReason.CompleteFillFailed, ); @@ -500,7 +500,7 @@ describe('Exchange wrappers', () => { await exchangeWrapper.fillOrKillOrderAsync(signedOrders[0], takerAddress); - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.batchFillOrKillOrdersAsync(signedOrders, takerAddress, { takerAssetFillAmounts, }), @@ -703,7 +703,7 @@ describe('Exchange wrappers', () => { orderFactory.newSignedOrder(), ]; - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.marketSellOrdersAsync(signedOrders, takerAddress, { takerAssetFillAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), 18), }), @@ -921,7 +921,7 @@ describe('Exchange wrappers', () => { orderFactory.newSignedOrder(), ]; - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( exchangeWrapper.marketBuyOrdersAsync(signedOrders, takerAddress, { makerAssetFillAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), 18), }), diff --git a/packages/contracts/test/libraries/lib_bytes.ts b/packages/contracts/test/libraries/lib_bytes.ts index 963b51b8f1..c80b61e198 100644 --- a/packages/contracts/test/libraries/lib_bytes.ts +++ b/packages/contracts/test/libraries/lib_bytes.ts @@ -9,7 +9,7 @@ import * as _ from 'lodash'; import { TestLibBytesContract } from '../../generated_contract_wrappers/test_lib_bytes'; import { artifacts } from '../utils/artifacts'; -import { expectRevertOrOtherErrorAsync } from '../utils/assertions'; +import { expectContractCallFailed } from '../utils/assertions'; import { chaiSetup } from '../utils/chai_setup'; import { constants } from '../utils/constants'; import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper'; @@ -100,7 +100,7 @@ describe('LibBytes', () => { describe('popLastByte', () => { it('should revert if length is 0', async () => { - return expectRevertOrOtherErrorAsync( + return expectContractCallFailed( libBytes.publicPopLastByte.callAsync(constants.NULL_BYTES), RevertReason.LibBytesGreaterThanZeroLengthRequired, ); @@ -116,7 +116,7 @@ describe('LibBytes', () => { describe('popLast20Bytes', () => { it('should revert if length is less than 20', async () => { - return expectRevertOrOtherErrorAsync( + return expectContractCallFailed( libBytes.publicPopLast20Bytes.callAsync(byteArrayShorterThan20Bytes), RevertReason.LibBytesGreaterOrEqualTo20LengthRequired, ); @@ -184,7 +184,7 @@ describe('LibBytes', () => { describe('deepCopyBytes', () => { it('should revert if dest is shorter than source', async () => { - return expectRevertOrOtherErrorAsync( + return expectContractCallFailed( libBytes.publicDeepCopyBytes.callAsync(byteArrayShorterThan32Bytes, byteArrayLongerThan32Bytes), RevertReason.LibBytesGreaterOrEqualToSourceBytesLengthRequired, ); @@ -237,7 +237,7 @@ describe('LibBytes', () => { it('should fail if the byte array is too short to hold an address', async () => { const shortByteArray = '0xabcdef'; const offset = new BigNumber(0); - return expectRevertOrOtherErrorAsync( + return expectContractCallFailed( libBytes.publicReadAddress.callAsync(shortByteArray, offset), 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 () => { const byteArray = testAddress; const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength); - return expectRevertOrOtherErrorAsync( + return expectContractCallFailed( libBytes.publicReadAddress.callAsync(byteArray, badOffset), RevertReason.LibBytesGreaterOrEqualTo20LengthRequired, ); @@ -281,7 +281,7 @@ describe('LibBytes', () => { }); it('should fail if the byte array is too short to hold an address', async () => { const offset = new BigNumber(0); - return expectRevertOrOtherErrorAsync( + return expectContractCallFailed( libBytes.publicWriteAddress.callAsync(byteArrayShorterThan20Bytes, offset, testAddress), 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 () => { const byteArray = byteArrayLongerThan32Bytes; const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength); - return expectRevertOrOtherErrorAsync( + return expectContractCallFailed( libBytes.publicWriteAddress.callAsync(byteArray, badOffset, testAddress), RevertReason.LibBytesGreaterOrEqualTo20LengthRequired, ); @@ -313,14 +313,14 @@ describe('LibBytes', () => { }); it('should fail if the byte array is too short to hold a bytes32', async () => { const offset = new BigNumber(0); - return expectRevertOrOtherErrorAsync( + return expectContractCallFailed( libBytes.publicReadBytes32.callAsync(byteArrayShorterThan32Bytes, offset), 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 () => { const badOffset = new BigNumber(ethUtil.toBuffer(testBytes32).byteLength); - return expectRevertOrOtherErrorAsync( + return expectContractCallFailed( libBytes.publicReadBytes32.callAsync(testBytes32, badOffset), RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, ); @@ -356,7 +356,7 @@ describe('LibBytes', () => { }); it('should fail if the byte array is too short to hold a bytes32', async () => { const offset = new BigNumber(0); - return expectRevertOrOtherErrorAsync( + return expectContractCallFailed( libBytes.publicWriteBytes32.callAsync(byteArrayShorterThan32Bytes, offset, testBytes32), 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 () => { const byteArray = byteArrayLongerThan32Bytes; const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength); - return expectRevertOrOtherErrorAsync( + return expectContractCallFailed( libBytes.publicWriteBytes32.callAsync(byteArray, badOffset, testBytes32), RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, ); @@ -392,7 +392,7 @@ describe('LibBytes', () => { }); it('should fail if the byte array is too short to hold a uint256', async () => { const offset = new BigNumber(0); - return expectRevertOrOtherErrorAsync( + return expectContractCallFailed( libBytes.publicReadUint256.callAsync(byteArrayShorterThan32Bytes, offset), RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, ); @@ -402,7 +402,7 @@ describe('LibBytes', () => { const testUint256AsBuffer = ethUtil.toBuffer(formattedTestUint256); const byteArray = ethUtil.bufferToHex(testUint256AsBuffer); const badOffset = new BigNumber(testUint256AsBuffer.byteLength); - return expectRevertOrOtherErrorAsync( + return expectContractCallFailed( libBytes.publicReadUint256.callAsync(byteArray, badOffset), RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, ); @@ -442,7 +442,7 @@ describe('LibBytes', () => { }); it('should fail if the byte array is too short to hold a uint256', async () => { const offset = new BigNumber(0); - return expectRevertOrOtherErrorAsync( + return expectContractCallFailed( libBytes.publicWriteUint256.callAsync(byteArrayShorterThan32Bytes, offset, testUint256), 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 () => { const byteArray = byteArrayLongerThan32Bytes; const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength); - return expectRevertOrOtherErrorAsync( + return expectContractCallFailed( libBytes.publicWriteUint256.callAsync(byteArray, badOffset, testUint256), 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' it('should revert if byte array has a length < 4', async () => { const byteArrayLessThan4Bytes = '0x010101'; - return expectRevertOrOtherErrorAsync( + return expectContractCallFailed( libBytes.publicReadBytes4.callAsync(byteArrayLessThan4Bytes, new BigNumber(0)), 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 () => { // 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); - return expectRevertOrOtherErrorAsync( + return expectContractCallFailed( libBytes.publicReadBytesWithLength.callAsync(byteArrayShorterThan32Bytes, offset), RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, ); }); it('should fail if we store a nested byte array length, without a nested byte array', async () => { const offset = new BigNumber(0); - return expectRevertOrOtherErrorAsync( + return expectContractCallFailed( libBytes.publicReadBytesWithLength.callAsync(testBytes32, offset), 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 () => { const badOffset = new BigNumber(ethUtil.toBuffer(byteArrayShorterThan32Bytes).byteLength); - return expectRevertOrOtherErrorAsync( + return expectContractCallFailed( libBytes.publicReadBytesWithLength.callAsync(byteArrayShorterThan32Bytes, badOffset), 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 () => { const badOffset = new BigNumber(ethUtil.toBuffer(testBytes32).byteLength); - return expectRevertOrOtherErrorAsync( + return expectContractCallFailed( libBytes.publicReadBytesWithLength.callAsync(testBytes32, badOffset), 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 () => { const offset = new BigNumber(0); const emptyByteArray = ethUtil.bufferToHex(new Buffer(1)); - return expectRevertOrOtherErrorAsync( + return expectContractCallFailed( libBytes.publicWriteBytesWithLength.callAsync(emptyByteArray, offset, longData), 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 () => { const emptyByteArray = ethUtil.bufferToHex(new Buffer(shortTestBytesAsBuffer.byteLength)); const badOffset = new BigNumber(ethUtil.toBuffer(shortTestBytesAsBuffer).byteLength); - return expectRevertOrOtherErrorAsync( + return expectContractCallFailed( libBytes.publicWriteBytesWithLength.callAsync(emptyByteArray, badOffset, shortData), RevertReason.LibBytesGreaterOrEqualToNestedBytesLengthRequired, ); diff --git a/packages/contracts/test/multisig/asset_proxy_owner.ts b/packages/contracts/test/multisig/asset_proxy_owner.ts index cde86dd463..16231dfcbd 100644 --- a/packages/contracts/test/multisig/asset_proxy_owner.ts +++ b/packages/contracts/test/multisig/asset_proxy_owner.ts @@ -14,8 +14,9 @@ import { MixinAuthorizableContract } from '../../generated_contract_wrappers/mix import { TestAssetProxyOwnerContract } from '../../generated_contract_wrappers/test_asset_proxy_owner'; import { artifacts } from '../utils/artifacts'; import { - expectRevertOrAlwaysFailingTransactionAsync, - expectRevertOrContractCallFailedAsync, + expectContractCallFailedWithoutReasonAsync, + expectContractCreationFailedWithoutReason, + expectTransactionFailedWithoutReasonAsync, } from '../utils/assertions'; import { chaiSetup } from '../utils/chai_setup'; import { constants } from '../utils/constants'; @@ -108,7 +109,7 @@ describe('AssetProxyOwner', () => { }); it('should throw if a null address is included in assetProxyContracts', async () => { const assetProxyContractAddresses = [erc20Proxy.address, constants.NULL_ADDRESS]; - return expectRevertOrAlwaysFailingTransactionAsync( + return expectContractCreationFailedWithoutReason( AssetProxyOwnerContract.deployFrom0xArtifactAsync( artifacts.AssetProxyOwner, provider, @@ -150,7 +151,7 @@ describe('AssetProxyOwner', () => { describe('registerAssetProxy', () => { it('should throw if not called by multisig', async () => { const isRegistered = true; - return expectRevertOrAlwaysFailingTransactionAsync( + return expectTransactionFailedWithoutReasonAsync( testAssetProxyOwner.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, isRegistered, { from: owners[0], }), @@ -277,7 +278,7 @@ describe('AssetProxyOwner', () => { ); const log = submitTxRes.logs[0] as LogWithDecodedArgs; const txId = log.args.transactionId; - return expectRevertOrContractCallFailedAsync( + return expectContractCallFailedWithoutReasonAsync( testAssetProxyOwner.testValidRemoveAuthorizedAddressAtIndexTx.callAsync(txId), ); }); @@ -312,7 +313,7 @@ describe('AssetProxyOwner', () => { ); const log = submitTxRes.logs[0] as LogWithDecodedArgs; const txId = log.args.transactionId; - return expectRevertOrContractCallFailedAsync( + return expectContractCallFailedWithoutReasonAsync( testAssetProxyOwner.testValidRemoveAuthorizedAddressAtIndexTx.callAsync(txId), ); }); @@ -332,7 +333,7 @@ describe('AssetProxyOwner', () => { const log = res.logs[0] as LogWithDecodedArgs; const txId = log.args.transactionId; - return expectRevertOrAlwaysFailingTransactionAsync( + return expectTransactionFailedWithoutReasonAsync( testAssetProxyOwner.executeRemoveAuthorizedAddressAtIndex.sendTransactionAsync(txId, { from: owners[1], }), @@ -354,7 +355,7 @@ describe('AssetProxyOwner', () => { await multiSigWrapper.confirmTransactionAsync(txId, owners[1]); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectTransactionFailedWithoutReasonAsync( testAssetProxyOwner.executeRemoveAuthorizedAddressAtIndex.sendTransactionAsync(txId, { from: owners[1], }), @@ -376,7 +377,7 @@ describe('AssetProxyOwner', () => { await multiSigWrapper.confirmTransactionAsync(txId, owners[1]); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectTransactionFailedWithoutReasonAsync( testAssetProxyOwner.executeRemoveAuthorizedAddressAtIndex.sendTransactionAsync(txId, { from: owners[1], }), @@ -433,7 +434,7 @@ describe('AssetProxyOwner', () => { const isExecuted = tx[3]; expect(isExecuted).to.equal(true); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectTransactionFailedWithoutReasonAsync( testAssetProxyOwner.executeRemoveAuthorizedAddressAtIndex.sendTransactionAsync(txId, { from: owners[1], }), diff --git a/packages/contracts/test/multisig/multi_sig_with_time_lock.ts b/packages/contracts/test/multisig/multi_sig_with_time_lock.ts index a746403d27..a7b99d8672 100644 --- a/packages/contracts/test/multisig/multi_sig_with_time_lock.ts +++ b/packages/contracts/test/multisig/multi_sig_with_time_lock.ts @@ -8,7 +8,7 @@ import { SubmissionContractEventArgs, } from '../../generated_contract_wrappers/multi_sig_wallet_with_time_lock'; import { artifacts } from '../utils/artifacts'; -import { expectRevertOrAlwaysFailingTransactionAsync } from '../utils/assertions'; +import { expectTransactionFailedWithoutReasonAsync } from '../utils/assertions'; import { chaiSetup } from '../utils/chai_setup'; import { constants } from '../utils/constants'; import { increaseTimeAndMineBlockAsync } from '../utils/increase_time'; @@ -67,7 +67,7 @@ describe('MultiSigWalletWithTimeLock', () => { }); it('should throw when not called by wallet', async () => { - return expectRevertOrAlwaysFailingTransactionAsync( + return expectTransactionFailedWithoutReasonAsync( 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 log = res.logs[0] as LogWithDecodedArgs; const txId = log.args.transactionId; - return expectRevertOrAlwaysFailingTransactionAsync( + return expectTransactionFailedWithoutReasonAsync( 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 () => { - return expectRevertOrAlwaysFailingTransactionAsync( + return expectTransactionFailedWithoutReasonAsync( multiSig.executeTransaction.sendTransactionAsync(txId, { from: owners[0] }), ); }); diff --git a/packages/contracts/test/token_registry.ts b/packages/contracts/test/token_registry.ts index 32f8cdee35..7cc43be9bb 100644 --- a/packages/contracts/test/token_registry.ts +++ b/packages/contracts/test/token_registry.ts @@ -7,7 +7,7 @@ import * as _ from 'lodash'; import { TokenRegistryContract } from '../generated_contract_wrappers/token_registry'; import { artifacts } from './utils/artifacts'; -import { expectRevertOrAlwaysFailingTransactionAsync } from './utils/assertions'; +import { expectTransactionFailedWithoutReasonAsync } from './utils/assertions'; import { chaiSetup } from './utils/chai_setup'; import { constants } from './utils/constants'; import { TokenRegWrapper } from './utils/token_registry_wrapper'; @@ -75,7 +75,7 @@ describe('TokenRegistry', () => { describe('addToken', () => { 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 () => { @@ -87,20 +87,18 @@ describe('TokenRegistry', () => { it('should throw if token already exists', async () => { 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 () => { - return expectRevertOrAlwaysFailingTransactionAsync(tokenRegWrapper.addTokenAsync(nullToken, owner)); + return expectTransactionFailedWithoutReasonAsync(tokenRegWrapper.addTokenAsync(nullToken, owner)); }); it('should throw if name already exists', async () => { await tokenRegWrapper.addTokenAsync(token1, owner); const duplicateNameToken = _.assign({}, token2, { name: token1.name }); - return expectRevertOrAlwaysFailingTransactionAsync( - tokenRegWrapper.addTokenAsync(duplicateNameToken, owner), - ); + return expectTransactionFailedWithoutReasonAsync(tokenRegWrapper.addTokenAsync(duplicateNameToken, owner)); }); it('should throw if symbol already exists', async () => { @@ -109,7 +107,7 @@ describe('TokenRegistry', () => { symbol: token1.symbol, }); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectTransactionFailedWithoutReasonAsync( tokenRegWrapper.addTokenAsync(duplicateSymbolToken, owner), ); }); @@ -136,7 +134,7 @@ describe('TokenRegistry', () => { describe('setTokenName', () => { it('should throw when not called by owner', async () => { - return expectRevertOrAlwaysFailingTransactionAsync( + return expectTransactionFailedWithoutReasonAsync( tokenReg.setTokenName.sendTransactionAsync(token1.address, token2.name, { from: notOwner }), ); }); @@ -162,13 +160,13 @@ describe('TokenRegistry', () => { it('should throw if the name already exists', async () => { await tokenRegWrapper.addTokenAsync(token2, owner); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectTransactionFailedWithoutReasonAsync( tokenReg.setTokenName.sendTransactionAsync(token1.address, token2.name, { from: owner }), ); }); it('should throw if token does not exist', async () => { - return expectRevertOrAlwaysFailingTransactionAsync( + return expectTransactionFailedWithoutReasonAsync( tokenReg.setTokenName.sendTransactionAsync(nullToken.address, token2.name, { from: owner }), ); }); @@ -176,7 +174,7 @@ describe('TokenRegistry', () => { describe('setTokenSymbol', () => { it('should throw when not called by owner', async () => { - return expectRevertOrAlwaysFailingTransactionAsync( + return expectTransactionFailedWithoutReasonAsync( tokenReg.setTokenSymbol.sendTransactionAsync(token1.address, token2.symbol, { from: notOwner, }), @@ -202,7 +200,7 @@ describe('TokenRegistry', () => { it('should throw if the symbol already exists', async () => { await tokenRegWrapper.addTokenAsync(token2, owner); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectTransactionFailedWithoutReasonAsync( tokenReg.setTokenSymbol.sendTransactionAsync(token1.address, token2.symbol, { from: owner, }), @@ -210,7 +208,7 @@ describe('TokenRegistry', () => { }); it('should throw if token does not exist', async () => { - return expectRevertOrAlwaysFailingTransactionAsync( + return expectTransactionFailedWithoutReasonAsync( tokenReg.setTokenSymbol.sendTransactionAsync(nullToken.address, token2.symbol, { from: owner, }), @@ -221,7 +219,7 @@ describe('TokenRegistry', () => { describe('removeToken', () => { it('should throw if not called by owner', async () => { const index = new BigNumber(0); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectTransactionFailedWithoutReasonAsync( tokenReg.removeToken.sendTransactionAsync(token1.address, index, { from: notOwner }), ); }); @@ -240,7 +238,7 @@ describe('TokenRegistry', () => { it('should throw if token does not exist', async () => { const index = new BigNumber(0); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectTransactionFailedWithoutReasonAsync( 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 () => { await tokenRegWrapper.addTokenAsync(token2, owner); const incorrectIndex = new BigNumber(0); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectTransactionFailedWithoutReasonAsync( tokenReg.removeToken.sendTransactionAsync(token2.address, incorrectIndex, { from: owner }), ); }); diff --git a/packages/contracts/test/tokens/ether_token.ts b/packages/contracts/test/tokens/ether_token.ts index 25ef155951..a104fc915e 100644 --- a/packages/contracts/test/tokens/ether_token.ts +++ b/packages/contracts/test/tokens/ether_token.ts @@ -5,7 +5,7 @@ import * as chai from 'chai'; import { WETH9Contract } from '../../generated_contract_wrappers/weth9'; import { artifacts } from '../utils/artifacts'; -import { expectInsufficientFundsAsync, expectRevertOrAlwaysFailingTransactionAsync } from '../utils/assertions'; +import { expectInsufficientFundsAsync, expectTransactionFailedWithoutReasonAsync } from '../utils/assertions'; import { chaiSetup } from '../utils/chai_setup'; import { constants } from '../utils/constants'; import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper'; @@ -74,7 +74,7 @@ describe('EtherToken', () => { const initEthTokenBalance = await etherToken.balanceOf.callAsync(account); const ethTokensToWithdraw = initEthTokenBalance.plus(1); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectTransactionFailedWithoutReasonAsync( etherToken.withdraw.sendTransactionAsync(ethTokensToWithdraw), ); }); diff --git a/packages/contracts/test/tokens/unlimited_allowance_token.ts b/packages/contracts/test/tokens/unlimited_allowance_token.ts index 09a24950cb..8a3b20d0fb 100644 --- a/packages/contracts/test/tokens/unlimited_allowance_token.ts +++ b/packages/contracts/test/tokens/unlimited_allowance_token.ts @@ -5,7 +5,7 @@ import * as chai from 'chai'; import { DummyERC20TokenContract } from '../../generated_contract_wrappers/dummy_e_r_c20_token'; import { artifacts } from '../utils/artifacts'; -import { expectRevertOrOtherErrorAsync } from '../utils/assertions'; +import { expectContractCallFailed } from '../utils/assertions'; import { chaiSetup } from '../utils/chai_setup'; import { constants } from '../utils/constants'; import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper'; @@ -54,7 +54,7 @@ describe('UnlimitedAllowanceToken', () => { it('should throw if owner has insufficient balance', async () => { const ownerBalance = await token.balanceOf.callAsync(owner); const amountToTransfer = ownerBalance.plus(1); - return expectRevertOrOtherErrorAsync( + return expectContractCallFailed( token.transfer.callAsync(spender, amountToTransfer, { from: owner }), RevertReason.Erc20InsufficientBalance, ); @@ -93,7 +93,7 @@ describe('UnlimitedAllowanceToken', () => { await token.approve.sendTransactionAsync(spender, amountToTransfer, { from: owner }), constants.AWAIT_TRANSACTION_MINED_MS, ); - return expectRevertOrOtherErrorAsync( + return expectContractCallFailed( token.transferFrom.callAsync(owner, spender, amountToTransfer, { from: spender, }), @@ -109,7 +109,7 @@ describe('UnlimitedAllowanceToken', () => { const isSpenderAllowanceInsufficient = spenderAllowance.cmp(amountToTransfer) < 0; expect(isSpenderAllowanceInsufficient).to.be.true(); - return expectRevertOrOtherErrorAsync( + return expectContractCallFailed( token.transferFrom.callAsync(owner, spender, amountToTransfer, { from: spender, }), diff --git a/packages/contracts/test/utils/assertions.ts b/packages/contracts/test/utils/assertions.ts index baba892d3c..112a470f60 100644 --- a/packages/contracts/test/utils/assertions.ts +++ b/packages/contracts/test/utils/assertions.ts @@ -1,108 +1,159 @@ import { RevertReason } from '@0xproject/types'; +import { logUtils } from '@0xproject/utils'; +import { NodeType } from '@0xproject/web3-wrapper'; import * as chai from 'chai'; -import { TransactionReceipt, TransactionReceiptWithDecodedLogs } from 'ethereum-types'; +import { TransactionReceipt, TransactionReceiptStatus, TransactionReceiptWithDecodedLogs } from 'ethereum-types'; import * as _ from 'lodash'; -import { constants } from './constants'; import { web3Wrapper } from './web3_wrapper'; const expect = chai.expect; -function _expectEitherErrorAsync(p: Promise, error1: string, error2: string): PromiseLike { - return expect(p) - .to.be.rejected() - .then(e => { - expect(e).to.satisfy( - (err: Error) => _.includes(err.message, error1) || _.includes(err.message, error2), - `expected promise to reject with error message that includes "${error1}" or "${error2}", but got: ` + - `"${e.message}"\n`, - ); - }); +let nodeType: NodeType | undefined; + +// Represents the return value of a `sendTransaction` call. The Promise should +// resolve with either a transaction receipt or a transaction hash. +export type sendTransactionResult = Promise; + +async function _getGanacheOrGethError(ganacheError: string, gethError: string): Promise { + 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 { + return _getGanacheOrGethError("sender doesn't have enough funds", 'insufficient funds'); +} + +async function _getTransactionFailedErrorMessageAsync(): Promise { + return _getGanacheOrGethError('revert', 'always failing transaction'); +} + +async function _getContractCallFailedErrorMessageAsync(): Promise { + return _getGanacheOrGethError('revert', 'Contract call failed'); } /** * Rejects if the given Promise does not reject with an error indicating * 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 * otherwise resolve with no value. */ -export function expectInsufficientFundsAsync(p: Promise): PromiseLike { - return _expectEitherErrorAsync(p, 'insufficient funds', "sender doesn't have enough funds"); +export async function expectInsufficientFundsAsync(p: Promise): Promise { + 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 - * given otherError. - * @param p the Promise which is expected to reject - * @param otherError the other error which is accepted as a valid reject error. - * @returns a new Promise which will reject if the conditions are not met and - * otherwise resolve with no value. - */ -export function expectRevertOrOtherErrorAsync(p: Promise, otherError: string): PromiseLike { - 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(p: Promise): PromiseLike { - 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 + * Resolves if the the sendTransaction call fails with the given revert reason. + * However, since Geth does not support revert reasons for sendTransaction, this + * falls back to expectTransactionFailedWithoutReasonAsync if the backing + * Ethereum node is Geth. + * @param p a Promise resulting from a sendTransaction call * @param reason a specific revert reason * @returns a new Promise which will reject if the conditions are not met and * otherwise resolve with no value. */ -export async function expectRevertReasonOrAlwaysFailingTransactionAsync( - p: Promise, - reason: RevertReason, -): Promise { +export async function expectTransactionFailedAsync(p: sendTransactionResult, reason: RevertReason): Promise { + // HACK(albrow): This dummy `catch` should not be necessary, but if you + // remove it, there is an uncaught exception and the Node process will + // 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 { return p .then(async result => { - let txReceiptStatus: string | 0 | 1 | null; - if (typeof result === 'string') { - // Result is a txHash. We need to make a web3 call to get the receipt. + let txReceiptStatus: TransactionReceiptStatus; + if (_.isString(result)) { + // 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); txReceiptStatus = txReceipt.status; } else if ('status' in result) { - // Result is a TransactionReceiptWithDecodedLogs or TransactionReceipt - // and status is a field of result. + // Result is a transaction receipt, so we can get the status + // directly. txReceiptStatus = result.status; } else { - throw new Error('Unexpected result type'); + throw new Error('Unexpected result type: ' + typeof result); } expect(_.toString(txReceiptStatus)).to.equal( '0', - 'transactionReceipt had a non-zero status, indicating success', + 'Expected transaction to fail but receipt had a non-zero status, indicating success', ); }) - .catch(err => { - expect(err.message).to.satisfy( - (msg: string) => _.includes(msg, reason) || _.includes(msg, 'always failing transaction'), - `Expected ${reason} or 'always failing transaction' but error message was ${err.message}`, - ); + .catch(async err => { + // If the promise rejects, we expect a specific error message, + // depending on the backing Ethereum node type. + const errMessage = await _getTransactionFailedErrorMessageAsync(); + expect(err.message).to.include(errMessage); }); } /** - * Rejects if the given Promise does not reject with a "revert" or "Contract - * call failed" error. - * @param p the Promise which is expected to reject + * Resolves if the the contract call fails with the given revert reason. + * @param p a Promise resulting from a contract call + * @param reason a specific revert reason * @returns a new Promise which will reject if the conditions are not met and * otherwise resolve with no value. */ -export function expectRevertOrContractCallFailedAsync(p: Promise): PromiseLike { - return expectRevertOrOtherErrorAsync(p, 'Contract call failed'); +export async function expectContractCallFailed(p: Promise, reason: RevertReason): Promise { + 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(p: Promise): Promise { + 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(p: Promise): Promise { + const errMessage = await _getTransactionFailedErrorMessageAsync(); + return expect(p).to.be.rejectedWith(errMessage); } diff --git a/packages/contracts/test/utils/constants.ts b/packages/contracts/test/utils/constants.ts index 8e68f376d2..7f3ad62e1c 100644 --- a/packages/contracts/test/utils/constants.ts +++ b/packages/contracts/test/utils/constants.ts @@ -18,7 +18,6 @@ const TESTRPC_PRIVATE_KEYS_STRINGS = [ export const constants = { INVALID_OPCODE: 'invalid opcode', - REVERT: 'revert', TESTRPC_NETWORK_ID: 50, // 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 diff --git a/packages/contracts/test/utils/core_combinatorial_utils.ts b/packages/contracts/test/utils/core_combinatorial_utils.ts index 03b09521d3..8c6c83014b 100644 --- a/packages/contracts/test/utils/core_combinatorial_utils.ts +++ b/packages/contracts/test/utils/core_combinatorial_utils.ts @@ -17,7 +17,7 @@ import 'make-promises-safe'; import { ExchangeContract, FillContractEventArgs } from '../../generated_contract_wrappers/exchange'; import { artifacts } from './artifacts'; -import { expectRevertReasonOrAlwaysFailingTransactionAsync } from './assertions'; +import { expectTransactionFailedAsync } from './assertions'; import { AssetWrapper } from './asset_wrapper'; import { chaiSetup } from './chai_setup'; import { constants } from './constants'; @@ -75,7 +75,7 @@ export async function coreCombinatorialUtilsFactoryAsync( const zrxAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address); const erc20FiveDecimalTokenCount = 2; - const fiveDecimals = new BigNumber(18); + const fiveDecimals = new BigNumber(5); const [erc20FiveDecimalTokenA, erc20FiveDecimalTokenB] = await erc20Wrapper.deployDummyTokensAsync( erc20FiveDecimalTokenCount, fiveDecimals, @@ -418,7 +418,7 @@ export class CoreCombinatorialUtils { fillRevertReasonIfExists: RevertReason | undefined, ): Promise { if (!_.isUndefined(fillRevertReasonIfExists)) { - return expectRevertReasonOrAlwaysFailingTransactionAsync( + return expectTransactionFailedAsync( this.exchangeWrapper.fillOrderAsync(signedOrder, this.takerAddress, { takerAssetFillAmount }), fillRevertReasonIfExists, ); diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index e620750543..15f8d6ab07 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -48,12 +48,11 @@ "@0xproject/subproviders": "^0.10.4", "@0xproject/types": "^0.8.1", "@0xproject/utils": "^0.7.1", - "ethereum-types": "^0.0.1", + "ethereum-types": "^0.0.2", "@0xproject/typescript-typings": "^0.4.1", "@0xproject/web3-wrapper": "^0.7.1", "lodash": "^4.17.4", - "web3": "^0.20.0", - "web3-provider-engine": "^14.0.4" + "web3-provider-engine": "14.0.6" }, "publishConfig": { "access": "public" diff --git a/packages/dev-utils/src/blockchain_lifecycle.ts b/packages/dev-utils/src/blockchain_lifecycle.ts index 587332f1a7..9bd65ee5d0 100644 --- a/packages/dev-utils/src/blockchain_lifecycle.ts +++ b/packages/dev-utils/src/blockchain_lifecycle.ts @@ -1,11 +1,6 @@ import { logUtils } from '@0xproject/utils'; -import { uniqueVersionIds, Web3Wrapper } from '@0xproject/web3-wrapper'; -import { includes } from 'lodash'; - -enum NodeType { - Geth = 'GETH', - Ganache = 'GANACHE', -} +import { NodeType, Web3Wrapper } from '@0xproject/web3-wrapper'; +import * as _ from 'lodash'; // 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 @@ -18,6 +13,7 @@ export class BlockchainLifecycle { private _web3Wrapper: Web3Wrapper; private _snapshotIdsStack: number[]; private _addresses: string[] = []; + private _nodeType: NodeType | undefined; constructor(web3Wrapper: Web3Wrapper) { this._web3Wrapper = web3Wrapper; this._snapshotIdsStack = []; @@ -61,16 +57,6 @@ export class BlockchainLifecycle { throw new Error(`Unknown node type: ${nodeType}`); } } - private async _getNodeTypeAsync(): Promise { - 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 { logUtils.warn('WARNING: minimum block number for tests not met. Mining additional blocks...'); if (this._addresses.length === 0) { @@ -92,4 +78,10 @@ export class BlockchainLifecycle { } logUtils.warn('Done mining the minimum number of blocks.'); } + private async _getNodeTypeAsync(): Promise { + if (_.isUndefined(this._nodeType)) { + this._nodeType = await this._web3Wrapper.getNodeTypeAsync(); + } + return this._nodeType; + } } diff --git a/packages/dev-utils/src/web3_factory.ts b/packages/dev-utils/src/web3_factory.ts index 47eef4cbd2..362a6c3c60 100644 --- a/packages/dev-utils/src/web3_factory.ts +++ b/packages/dev-utils/src/web3_factory.ts @@ -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 RpcSubprovider = require('web3-provider-engine/subproviders/rpc'); diff --git a/packages/ethereum-types/CHANGELOG.json b/packages/ethereum-types/CHANGELOG.json index ad532062f7..351e9c23f9 100644 --- a/packages/ethereum-types/CHANGELOG.json +++ b/packages/ethereum-types/CHANGELOG.json @@ -5,6 +5,10 @@ { "note": "Add `TraceParams` interface for `debug_traceTransaction` parameters", "pr": 675 + }, + { + "note": "Add `TransactionReceiptStatus` type", + "pr": 812 } ] }, diff --git a/packages/ethereum-types/package.json b/packages/ethereum-types/package.json index a937f6ad10..9d49308263 100644 --- a/packages/ethereum-types/package.json +++ b/packages/ethereum-types/package.json @@ -1,6 +1,6 @@ { "name": "ethereum-types", - "version": "0.0.1", + "version": "0.0.2", "engines": { "node": ">=6.12" }, diff --git a/packages/ethereum-types/src/index.ts b/packages/ethereum-types/src/index.ts index 499c843275..f4d445e3be 100644 --- a/packages/ethereum-types/src/index.ts +++ b/packages/ethereum-types/src/index.ts @@ -215,6 +215,8 @@ export interface TxDataPayable extends TxData { value?: BigNumber; } +export type TransactionReceiptStatus = null | string | 0 | 1; + export interface TransactionReceipt { blockHash: string; blockNumber: number; @@ -222,7 +224,7 @@ export interface TransactionReceipt { transactionIndex: number; from: string; to: string; - status: null | string | 0 | 1; + status: TransactionReceiptStatus; cumulativeGasUsed: number; gasUsed: number; contractAddress: string | null; diff --git a/packages/fill-scenarios/CHANGELOG.json b/packages/fill-scenarios/CHANGELOG.json index ec9111ebb8..822f8d5d5a 100644 --- a/packages/fill-scenarios/CHANGELOG.json +++ b/packages/fill-scenarios/CHANGELOG.json @@ -1,6 +1,6 @@ [ { - "version": "0.1.0", + "version": "1.0.0", "changes": [ { "note": "Make fill-scenarios compatible with V2 of 0x protocol", diff --git a/packages/fill-scenarios/package.json b/packages/fill-scenarios/package.json index 883300fa07..91024ed345 100644 --- a/packages/fill-scenarios/package.json +++ b/packages/fill-scenarios/package.json @@ -44,7 +44,7 @@ "@0xproject/base-contract": "^0.3.4", "@0xproject/order-utils": "^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/utils": "^0.7.1", "@0xproject/web3-wrapper": "^0.7.1", diff --git a/packages/json-schemas/CHANGELOG.json b/packages/json-schemas/CHANGELOG.json index bb9cd3cd22..2b37e18366 100644 --- a/packages/json-schemas/CHANGELOG.json +++ b/packages/json-schemas/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "1.0.0", + "changes": [ + { + "note": "Update schemas for V2", + "pr": 615 + } + ] + }, { "timestamp": 1529397769, "version": "0.8.1", diff --git a/packages/metacoin/package.json b/packages/metacoin/package.json index 6b6ab9c407..3fa79387ef 100644 --- a/packages/metacoin/package.json +++ b/packages/metacoin/package.json @@ -45,7 +45,7 @@ "ethers": "3.0.22", "lodash": "^4.17.4", "run-s": "^0.0.0", - "web3-provider-engine": "^14.0.4" + "web3-provider-engine": "14.0.6" }, "devDependencies": { "@0xproject/dev-utils": "^0.4.4", diff --git a/packages/migrations/artifacts/2.0.0/ZRXToken.json b/packages/migrations/artifacts/2.0.0/ZRXToken.json index cad596044b..2078bb4d6d 100644 --- a/packages/migrations/artifacts/2.0.0/ZRXToken.json +++ b/packages/migrations/artifacts/2.0.0/ZRXToken.json @@ -228,39 +228,39 @@ "evm": { "bytecode": { "linkReferences": {}, - "object": "0x60806040526b033b2e3c9fd0803ce800000060035534801561002057600080fd5b506003543360009081526020819052604090205561067b806100436000396000f3006080604052600436106100985763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166306fdde03811461009d578063095ea7b31461012757806318160ddd1461016c57806323b872dd14610193578063313ce567146101ca57806370a08231146101f557806395d89b4114610223578063a9059cbb14610238578063dd62ed3e14610269575b600080fd5b3480156100a957600080fd5b506100b261029d565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100ec5781810151838201526020016100d4565b50505050905090810190601f1680156101195780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561013357600080fd5b5061015873ffffffffffffffffffffffffffffffffffffffff600435166024356102d4565b604080519115158252519081900360200190f35b34801561017857600080fd5b50610181610348565b60408051918252519081900360200190f35b34801561019f57600080fd5b5061015873ffffffffffffffffffffffffffffffffffffffff6004358116906024351660443561034e565b3480156101d657600080fd5b506101df6104e8565b6040805160ff9092168252519081900360200190f35b34801561020157600080fd5b5061018173ffffffffffffffffffffffffffffffffffffffff600435166104ed565b34801561022f57600080fd5b506100b2610515565b34801561024457600080fd5b5061015873ffffffffffffffffffffffffffffffffffffffff6004351660243561054c565b34801561027557600080fd5b5061018173ffffffffffffffffffffffffffffffffffffffff60043581169060243516610617565b60408051808201909152601181527f30782050726f746f636f6c20546f6b656e000000000000000000000000000000602082015281565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a35060015b92915050565b60035481565b73ffffffffffffffffffffffffffffffffffffffff8316600081815260016020908152604080832033845282528083205493835290829052812054909190831180159061039b5750828110155b80156103ce575073ffffffffffffffffffffffffffffffffffffffff841660009081526020819052604090205483810110155b156104db5773ffffffffffffffffffffffffffffffffffffffff808516600090815260208190526040808220805487019055918716815220805484900390557fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81101561046d5773ffffffffffffffffffffffffffffffffffffffff851660009081526001602090815260408083203384529091529020805484900390555b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a3600191506104e0565b600091505b509392505050565b601281565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b60408051808201909152600381527f5a52580000000000000000000000000000000000000000000000000000000000602082015281565b336000908152602081905260408120548211801590610592575073ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604090205482810110155b1561060f57336000818152602081815260408083208054879003905573ffffffffffffffffffffffffffffffffffffffff871680845292819020805487019055805186815290519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a3506001610342565b506000610342565b73ffffffffffffffffffffffffffffffffffffffff9182166000908152600160209081526040808320939094168252919091522054905600a165627a7a72305820a64d50ba7ca8023b64d00c49e38afa498258fdc2e4a694ef1c57201e9a757ec10029", - "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE PUSH12 0x33B2E3C9FD0803CE8000000 PUSH1 0x3 SSTORE CALLVALUE DUP1 ISZERO PUSH2 0x20 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x3 SLOAD CALLER PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SSTORE PUSH2 0x67B DUP1 PUSH2 0x43 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH2 0x98 JUMPI PUSH4 0xFFFFFFFF PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH1 0x0 CALLDATALOAD DIV AND PUSH4 0x6FDDE03 DUP2 EQ PUSH2 0x9D JUMPI DUP1 PUSH4 0x95EA7B3 EQ PUSH2 0x127 JUMPI DUP1 PUSH4 0x18160DDD EQ PUSH2 0x16C JUMPI DUP1 PUSH4 0x23B872DD EQ PUSH2 0x193 JUMPI DUP1 PUSH4 0x313CE567 EQ PUSH2 0x1CA JUMPI DUP1 PUSH4 0x70A08231 EQ PUSH2 0x1F5 JUMPI DUP1 PUSH4 0x95D89B41 EQ PUSH2 0x223 JUMPI DUP1 PUSH4 0xA9059CBB EQ PUSH2 0x238 JUMPI DUP1 PUSH4 0xDD62ED3E EQ PUSH2 0x269 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0xA9 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0xB2 PUSH2 0x29D JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 DUP1 DUP3 MSTORE DUP4 MLOAD DUP2 DUP4 ADD MSTORE DUP4 MLOAD SWAP2 SWAP3 DUP4 SWAP3 SWAP1 DUP4 ADD SWAP2 DUP6 ADD SWAP1 DUP1 DUP4 DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0xEC JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH2 0xD4 JUMP JUMPDEST POP POP POP POP SWAP1 POP SWAP1 DUP2 ADD SWAP1 PUSH1 0x1F AND DUP1 ISZERO PUSH2 0x119 JUMPI DUP1 DUP3 SUB DUP1 MLOAD PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP JUMPDEST POP SWAP3 POP POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x133 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x158 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD AND PUSH1 0x24 CALLDATALOAD PUSH2 0x2D4 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 ISZERO ISZERO DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x178 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x181 PUSH2 0x348 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x19F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x158 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD DUP2 AND SWAP1 PUSH1 0x24 CALLDATALOAD AND PUSH1 0x44 CALLDATALOAD PUSH2 0x34E JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1D6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1DF PUSH2 0x4E8 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0xFF SWAP1 SWAP3 AND DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x201 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x181 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD AND PUSH2 0x4ED JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x22F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0xB2 PUSH2 0x515 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x244 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x158 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD AND PUSH1 0x24 CALLDATALOAD PUSH2 0x54C JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x275 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x181 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD DUP2 AND SWAP1 PUSH1 0x24 CALLDATALOAD AND PUSH2 0x617 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP1 DUP3 ADD SWAP1 SWAP2 MSTORE PUSH1 0x11 DUP2 MSTORE PUSH32 0x30782050726F746F636F6C20546F6B656E000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE DUP2 JUMP JUMPDEST CALLER PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP8 AND DUP1 DUP6 MSTORE SWAP1 DUP4 MSTORE DUP2 DUP5 KECCAK256 DUP7 SWAP1 SSTORE DUP2 MLOAD DUP7 DUP2 MSTORE SWAP2 MLOAD SWAP4 SWAP5 SWAP1 SWAP4 SWAP1 SWAP3 PUSH32 0x8C5BE1E5EBEC7D5BD14F71427D1E84F3DD0314C0F7B2291E5B200AC8C7C3B925 SWAP3 DUP3 SWAP1 SUB ADD SWAP1 LOG3 POP PUSH1 0x1 JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x3 SLOAD DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 CALLER DUP5 MSTORE DUP3 MSTORE DUP1 DUP4 KECCAK256 SLOAD SWAP4 DUP4 MSTORE SWAP1 DUP3 SWAP1 MSTORE DUP2 KECCAK256 SLOAD SWAP1 SWAP2 SWAP1 DUP4 GT DUP1 ISZERO SWAP1 PUSH2 0x39B JUMPI POP DUP3 DUP2 LT ISZERO JUMPDEST DUP1 ISZERO PUSH2 0x3CE JUMPI POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD DUP4 DUP2 ADD LT ISZERO JUMPDEST ISZERO PUSH2 0x4DB JUMPI PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP6 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 DUP1 DUP3 KECCAK256 DUP1 SLOAD DUP8 ADD SWAP1 SSTORE SWAP2 DUP8 AND DUP2 MSTORE KECCAK256 DUP1 SLOAD DUP5 SWAP1 SUB SWAP1 SSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 LT ISZERO PUSH2 0x46D JUMPI PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP6 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 CALLER DUP5 MSTORE SWAP1 SWAP2 MSTORE SWAP1 KECCAK256 DUP1 SLOAD DUP5 SWAP1 SUB SWAP1 SSTORE JUMPDEST DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP6 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF DUP6 PUSH1 0x40 MLOAD DUP1 DUP3 DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG3 PUSH1 0x1 SWAP2 POP PUSH2 0x4E0 JUMP JUMPDEST PUSH1 0x0 SWAP2 POP JUMPDEST POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x12 DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD SWAP1 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP1 DUP3 ADD SWAP1 SWAP2 MSTORE PUSH1 0x3 DUP2 MSTORE PUSH32 0x5A52580000000000000000000000000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE DUP2 JUMP JUMPDEST CALLER PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 DUP2 KECCAK256 SLOAD DUP3 GT DUP1 ISZERO SWAP1 PUSH2 0x592 JUMPI POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD DUP3 DUP2 ADD LT ISZERO JUMPDEST ISZERO PUSH2 0x60F JUMPI CALLER PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x20 DUP2 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 DUP1 SLOAD DUP8 SWAP1 SUB SWAP1 SSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP8 AND DUP1 DUP5 MSTORE SWAP3 DUP2 SWAP1 KECCAK256 DUP1 SLOAD DUP8 ADD SWAP1 SSTORE DUP1 MLOAD DUP7 DUP2 MSTORE SWAP1 MLOAD SWAP3 SWAP4 SWAP3 PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF SWAP3 SWAP2 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG3 POP PUSH1 0x1 PUSH2 0x342 JUMP JUMPDEST POP PUSH1 0x0 PUSH2 0x342 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP2 DUP3 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP4 SWAP1 SWAP5 AND DUP3 MSTORE SWAP2 SWAP1 SWAP2 MSTORE KECCAK256 SLOAD SWAP1 JUMP STOP LOG1 PUSH6 0x627A7A723058 KECCAK256 0xa6 0x4d POP 0xba PUSH29 0xA8023B64D00C49E38AFA498258FDC2E4A694EF1C57201E9A757EC10029 ", - "sourceMap": "753:342:0:-;;;872:6;846:32;;1022:71;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1075:11:0;;1061:10;1052:8;:20;;;;;;;;;;:34;753:342;;;;;;" + "object": "0x60806040526b033b2e3c9fd0803ce800000060035534801561002057600080fd5b506003543360009081526020819052604090205561067b806100436000396000f3006080604052600436106100985763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166306fdde03811461009d578063095ea7b31461012757806318160ddd1461016c57806323b872dd14610193578063313ce567146101ca57806370a08231146101f557806395d89b4114610223578063a9059cbb14610238578063dd62ed3e14610269575b600080fd5b3480156100a957600080fd5b506100b261029d565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100ec5781810151838201526020016100d4565b50505050905090810190601f1680156101195780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561013357600080fd5b5061015873ffffffffffffffffffffffffffffffffffffffff600435166024356102d4565b604080519115158252519081900360200190f35b34801561017857600080fd5b50610181610348565b60408051918252519081900360200190f35b34801561019f57600080fd5b5061015873ffffffffffffffffffffffffffffffffffffffff6004358116906024351660443561034e565b3480156101d657600080fd5b506101df6104e8565b6040805160ff9092168252519081900360200190f35b34801561020157600080fd5b5061018173ffffffffffffffffffffffffffffffffffffffff600435166104ed565b34801561022f57600080fd5b506100b2610515565b34801561024457600080fd5b5061015873ffffffffffffffffffffffffffffffffffffffff6004351660243561054c565b34801561027557600080fd5b5061018173ffffffffffffffffffffffffffffffffffffffff60043581169060243516610617565b60408051808201909152601181527f30782050726f746f636f6c20546f6b656e000000000000000000000000000000602082015281565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a35060015b92915050565b60035481565b73ffffffffffffffffffffffffffffffffffffffff8316600081815260016020908152604080832033845282528083205493835290829052812054909190831180159061039b5750828110155b80156103ce575073ffffffffffffffffffffffffffffffffffffffff841660009081526020819052604090205483810110155b156104db5773ffffffffffffffffffffffffffffffffffffffff808516600090815260208190526040808220805487019055918716815220805484900390557fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81101561046d5773ffffffffffffffffffffffffffffffffffffffff851660009081526001602090815260408083203384529091529020805484900390555b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a3600191506104e0565b600091505b509392505050565b601281565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b60408051808201909152600381527f5a52580000000000000000000000000000000000000000000000000000000000602082015281565b336000908152602081905260408120548211801590610592575073ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604090205482810110155b1561060f57336000818152602081815260408083208054879003905573ffffffffffffffffffffffffffffffffffffffff871680845292819020805487019055805186815290519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a3506001610342565b506000610342565b73ffffffffffffffffffffffffffffffffffffffff9182166000908152600160209081526040808320939094168252919091522054905600a165627a7a72305820d9d363d7eef4f71b2db22174233e9d08dc044f8bfd3ec76c93b896b38fefdf0b0029", + "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE PUSH12 0x33B2E3C9FD0803CE8000000 PUSH1 0x3 SSTORE CALLVALUE DUP1 ISZERO PUSH2 0x20 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x3 SLOAD CALLER PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SSTORE PUSH2 0x67B DUP1 PUSH2 0x43 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH2 0x98 JUMPI PUSH4 0xFFFFFFFF PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH1 0x0 CALLDATALOAD DIV AND PUSH4 0x6FDDE03 DUP2 EQ PUSH2 0x9D JUMPI DUP1 PUSH4 0x95EA7B3 EQ PUSH2 0x127 JUMPI DUP1 PUSH4 0x18160DDD EQ PUSH2 0x16C JUMPI DUP1 PUSH4 0x23B872DD EQ PUSH2 0x193 JUMPI DUP1 PUSH4 0x313CE567 EQ PUSH2 0x1CA JUMPI DUP1 PUSH4 0x70A08231 EQ PUSH2 0x1F5 JUMPI DUP1 PUSH4 0x95D89B41 EQ PUSH2 0x223 JUMPI DUP1 PUSH4 0xA9059CBB EQ PUSH2 0x238 JUMPI DUP1 PUSH4 0xDD62ED3E EQ PUSH2 0x269 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0xA9 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0xB2 PUSH2 0x29D JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 DUP1 DUP3 MSTORE DUP4 MLOAD DUP2 DUP4 ADD MSTORE DUP4 MLOAD SWAP2 SWAP3 DUP4 SWAP3 SWAP1 DUP4 ADD SWAP2 DUP6 ADD SWAP1 DUP1 DUP4 DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0xEC JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH2 0xD4 JUMP JUMPDEST POP POP POP POP SWAP1 POP SWAP1 DUP2 ADD SWAP1 PUSH1 0x1F AND DUP1 ISZERO PUSH2 0x119 JUMPI DUP1 DUP3 SUB DUP1 MLOAD PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP JUMPDEST POP SWAP3 POP POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x133 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x158 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD AND PUSH1 0x24 CALLDATALOAD PUSH2 0x2D4 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 ISZERO ISZERO DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x178 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x181 PUSH2 0x348 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x19F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x158 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD DUP2 AND SWAP1 PUSH1 0x24 CALLDATALOAD AND PUSH1 0x44 CALLDATALOAD PUSH2 0x34E JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1D6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1DF PUSH2 0x4E8 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0xFF SWAP1 SWAP3 AND DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x201 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x181 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD AND PUSH2 0x4ED JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x22F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0xB2 PUSH2 0x515 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x244 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x158 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD AND PUSH1 0x24 CALLDATALOAD PUSH2 0x54C JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x275 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x181 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD DUP2 AND SWAP1 PUSH1 0x24 CALLDATALOAD AND PUSH2 0x617 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP1 DUP3 ADD SWAP1 SWAP2 MSTORE PUSH1 0x11 DUP2 MSTORE PUSH32 0x30782050726F746F636F6C20546F6B656E000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE DUP2 JUMP JUMPDEST CALLER PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP8 AND DUP1 DUP6 MSTORE SWAP1 DUP4 MSTORE DUP2 DUP5 KECCAK256 DUP7 SWAP1 SSTORE DUP2 MLOAD DUP7 DUP2 MSTORE SWAP2 MLOAD SWAP4 SWAP5 SWAP1 SWAP4 SWAP1 SWAP3 PUSH32 0x8C5BE1E5EBEC7D5BD14F71427D1E84F3DD0314C0F7B2291E5B200AC8C7C3B925 SWAP3 DUP3 SWAP1 SUB ADD SWAP1 LOG3 POP PUSH1 0x1 JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x3 SLOAD DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 CALLER DUP5 MSTORE DUP3 MSTORE DUP1 DUP4 KECCAK256 SLOAD SWAP4 DUP4 MSTORE SWAP1 DUP3 SWAP1 MSTORE DUP2 KECCAK256 SLOAD SWAP1 SWAP2 SWAP1 DUP4 GT DUP1 ISZERO SWAP1 PUSH2 0x39B JUMPI POP DUP3 DUP2 LT ISZERO JUMPDEST DUP1 ISZERO PUSH2 0x3CE JUMPI POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD DUP4 DUP2 ADD LT ISZERO JUMPDEST ISZERO PUSH2 0x4DB JUMPI PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP6 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 DUP1 DUP3 KECCAK256 DUP1 SLOAD DUP8 ADD SWAP1 SSTORE SWAP2 DUP8 AND DUP2 MSTORE KECCAK256 DUP1 SLOAD DUP5 SWAP1 SUB SWAP1 SSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 LT ISZERO PUSH2 0x46D JUMPI PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP6 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 CALLER DUP5 MSTORE SWAP1 SWAP2 MSTORE SWAP1 KECCAK256 DUP1 SLOAD DUP5 SWAP1 SUB SWAP1 SSTORE JUMPDEST DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP6 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF DUP6 PUSH1 0x40 MLOAD DUP1 DUP3 DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG3 PUSH1 0x1 SWAP2 POP PUSH2 0x4E0 JUMP JUMPDEST PUSH1 0x0 SWAP2 POP JUMPDEST POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x12 DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD SWAP1 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP1 DUP3 ADD SWAP1 SWAP2 MSTORE PUSH1 0x3 DUP2 MSTORE PUSH32 0x5A52580000000000000000000000000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE DUP2 JUMP JUMPDEST CALLER PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 DUP2 KECCAK256 SLOAD DUP3 GT DUP1 ISZERO SWAP1 PUSH2 0x592 JUMPI POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD DUP3 DUP2 ADD LT ISZERO JUMPDEST ISZERO PUSH2 0x60F JUMPI CALLER PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x20 DUP2 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 DUP1 SLOAD DUP8 SWAP1 SUB SWAP1 SSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP8 AND DUP1 DUP5 MSTORE SWAP3 DUP2 SWAP1 KECCAK256 DUP1 SLOAD DUP8 ADD SWAP1 SSTORE DUP1 MLOAD DUP7 DUP2 MSTORE SWAP1 MLOAD SWAP3 SWAP4 SWAP3 PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF SWAP3 SWAP2 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG3 POP PUSH1 0x1 PUSH2 0x342 JUMP JUMPDEST POP PUSH1 0x0 PUSH2 0x342 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP2 DUP3 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP4 SWAP1 SWAP5 AND DUP3 MSTORE SWAP2 SWAP1 SWAP2 MSTORE KECCAK256 SLOAD SWAP1 JUMP STOP LOG1 PUSH6 0x627A7A723058 KECCAK256 0xd9 0xd3 PUSH4 0xD7EEF4F7 SHL 0x2d 0xb2 0x21 PUSH21 0x233E9D08DC044F8BFD3EC76C93B896B38FEFDF0B00 0x29 ", + "sourceMap": "750:342:3:-;;;869:6;843:32;;1019:71;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1072:11:3;;1058:10;1049:8;:20;;;;;;;;;;:34;750:342;;;;;;" }, "deployedBytecode": { "linkReferences": {}, - "object": "0x6080604052600436106100985763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166306fdde03811461009d578063095ea7b31461012757806318160ddd1461016c57806323b872dd14610193578063313ce567146101ca57806370a08231146101f557806395d89b4114610223578063a9059cbb14610238578063dd62ed3e14610269575b600080fd5b3480156100a957600080fd5b506100b261029d565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100ec5781810151838201526020016100d4565b50505050905090810190601f1680156101195780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561013357600080fd5b5061015873ffffffffffffffffffffffffffffffffffffffff600435166024356102d4565b604080519115158252519081900360200190f35b34801561017857600080fd5b50610181610348565b60408051918252519081900360200190f35b34801561019f57600080fd5b5061015873ffffffffffffffffffffffffffffffffffffffff6004358116906024351660443561034e565b3480156101d657600080fd5b506101df6104e8565b6040805160ff9092168252519081900360200190f35b34801561020157600080fd5b5061018173ffffffffffffffffffffffffffffffffffffffff600435166104ed565b34801561022f57600080fd5b506100b2610515565b34801561024457600080fd5b5061015873ffffffffffffffffffffffffffffffffffffffff6004351660243561054c565b34801561027557600080fd5b5061018173ffffffffffffffffffffffffffffffffffffffff60043581169060243516610617565b60408051808201909152601181527f30782050726f746f636f6c20546f6b656e000000000000000000000000000000602082015281565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a35060015b92915050565b60035481565b73ffffffffffffffffffffffffffffffffffffffff8316600081815260016020908152604080832033845282528083205493835290829052812054909190831180159061039b5750828110155b80156103ce575073ffffffffffffffffffffffffffffffffffffffff841660009081526020819052604090205483810110155b156104db5773ffffffffffffffffffffffffffffffffffffffff808516600090815260208190526040808220805487019055918716815220805484900390557fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81101561046d5773ffffffffffffffffffffffffffffffffffffffff851660009081526001602090815260408083203384529091529020805484900390555b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a3600191506104e0565b600091505b509392505050565b601281565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b60408051808201909152600381527f5a52580000000000000000000000000000000000000000000000000000000000602082015281565b336000908152602081905260408120548211801590610592575073ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604090205482810110155b1561060f57336000818152602081815260408083208054879003905573ffffffffffffffffffffffffffffffffffffffff871680845292819020805487019055805186815290519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a3506001610342565b506000610342565b73ffffffffffffffffffffffffffffffffffffffff9182166000908152600160209081526040808320939094168252919091522054905600a165627a7a72305820a64d50ba7ca8023b64d00c49e38afa498258fdc2e4a694ef1c57201e9a757ec10029", - "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH2 0x98 JUMPI PUSH4 0xFFFFFFFF PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH1 0x0 CALLDATALOAD DIV AND PUSH4 0x6FDDE03 DUP2 EQ PUSH2 0x9D JUMPI DUP1 PUSH4 0x95EA7B3 EQ PUSH2 0x127 JUMPI DUP1 PUSH4 0x18160DDD EQ PUSH2 0x16C JUMPI DUP1 PUSH4 0x23B872DD EQ PUSH2 0x193 JUMPI DUP1 PUSH4 0x313CE567 EQ PUSH2 0x1CA JUMPI DUP1 PUSH4 0x70A08231 EQ PUSH2 0x1F5 JUMPI DUP1 PUSH4 0x95D89B41 EQ PUSH2 0x223 JUMPI DUP1 PUSH4 0xA9059CBB EQ PUSH2 0x238 JUMPI DUP1 PUSH4 0xDD62ED3E EQ PUSH2 0x269 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0xA9 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0xB2 PUSH2 0x29D JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 DUP1 DUP3 MSTORE DUP4 MLOAD DUP2 DUP4 ADD MSTORE DUP4 MLOAD SWAP2 SWAP3 DUP4 SWAP3 SWAP1 DUP4 ADD SWAP2 DUP6 ADD SWAP1 DUP1 DUP4 DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0xEC JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH2 0xD4 JUMP JUMPDEST POP POP POP POP SWAP1 POP SWAP1 DUP2 ADD SWAP1 PUSH1 0x1F AND DUP1 ISZERO PUSH2 0x119 JUMPI DUP1 DUP3 SUB DUP1 MLOAD PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP JUMPDEST POP SWAP3 POP POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x133 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x158 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD AND PUSH1 0x24 CALLDATALOAD PUSH2 0x2D4 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 ISZERO ISZERO DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x178 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x181 PUSH2 0x348 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x19F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x158 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD DUP2 AND SWAP1 PUSH1 0x24 CALLDATALOAD AND PUSH1 0x44 CALLDATALOAD PUSH2 0x34E JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1D6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1DF PUSH2 0x4E8 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0xFF SWAP1 SWAP3 AND DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x201 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x181 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD AND PUSH2 0x4ED JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x22F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0xB2 PUSH2 0x515 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x244 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x158 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD AND PUSH1 0x24 CALLDATALOAD PUSH2 0x54C JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x275 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x181 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD DUP2 AND SWAP1 PUSH1 0x24 CALLDATALOAD AND PUSH2 0x617 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP1 DUP3 ADD SWAP1 SWAP2 MSTORE PUSH1 0x11 DUP2 MSTORE PUSH32 0x30782050726F746F636F6C20546F6B656E000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE DUP2 JUMP JUMPDEST CALLER PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP8 AND DUP1 DUP6 MSTORE SWAP1 DUP4 MSTORE DUP2 DUP5 KECCAK256 DUP7 SWAP1 SSTORE DUP2 MLOAD DUP7 DUP2 MSTORE SWAP2 MLOAD SWAP4 SWAP5 SWAP1 SWAP4 SWAP1 SWAP3 PUSH32 0x8C5BE1E5EBEC7D5BD14F71427D1E84F3DD0314C0F7B2291E5B200AC8C7C3B925 SWAP3 DUP3 SWAP1 SUB ADD SWAP1 LOG3 POP PUSH1 0x1 JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x3 SLOAD DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 CALLER DUP5 MSTORE DUP3 MSTORE DUP1 DUP4 KECCAK256 SLOAD SWAP4 DUP4 MSTORE SWAP1 DUP3 SWAP1 MSTORE DUP2 KECCAK256 SLOAD SWAP1 SWAP2 SWAP1 DUP4 GT DUP1 ISZERO SWAP1 PUSH2 0x39B JUMPI POP DUP3 DUP2 LT ISZERO JUMPDEST DUP1 ISZERO PUSH2 0x3CE JUMPI POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD DUP4 DUP2 ADD LT ISZERO JUMPDEST ISZERO PUSH2 0x4DB JUMPI PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP6 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 DUP1 DUP3 KECCAK256 DUP1 SLOAD DUP8 ADD SWAP1 SSTORE SWAP2 DUP8 AND DUP2 MSTORE KECCAK256 DUP1 SLOAD DUP5 SWAP1 SUB SWAP1 SSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 LT ISZERO PUSH2 0x46D JUMPI PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP6 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 CALLER DUP5 MSTORE SWAP1 SWAP2 MSTORE SWAP1 KECCAK256 DUP1 SLOAD DUP5 SWAP1 SUB SWAP1 SSTORE JUMPDEST DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP6 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF DUP6 PUSH1 0x40 MLOAD DUP1 DUP3 DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG3 PUSH1 0x1 SWAP2 POP PUSH2 0x4E0 JUMP JUMPDEST PUSH1 0x0 SWAP2 POP JUMPDEST POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x12 DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD SWAP1 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP1 DUP3 ADD SWAP1 SWAP2 MSTORE PUSH1 0x3 DUP2 MSTORE PUSH32 0x5A52580000000000000000000000000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE DUP2 JUMP JUMPDEST CALLER PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 DUP2 KECCAK256 SLOAD DUP3 GT DUP1 ISZERO SWAP1 PUSH2 0x592 JUMPI POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD DUP3 DUP2 ADD LT ISZERO JUMPDEST ISZERO PUSH2 0x60F JUMPI CALLER PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x20 DUP2 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 DUP1 SLOAD DUP8 SWAP1 SUB SWAP1 SSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP8 AND DUP1 DUP5 MSTORE SWAP3 DUP2 SWAP1 KECCAK256 DUP1 SLOAD DUP8 ADD SWAP1 SSTORE DUP1 MLOAD DUP7 DUP2 MSTORE SWAP1 MLOAD SWAP3 SWAP4 SWAP3 PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF SWAP3 SWAP2 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG3 POP PUSH1 0x1 PUSH2 0x342 JUMP JUMPDEST POP PUSH1 0x0 PUSH2 0x342 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP2 DUP3 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP4 SWAP1 SWAP5 AND DUP3 MSTORE SWAP2 SWAP1 SWAP2 MSTORE KECCAK256 SLOAD SWAP1 JUMP STOP LOG1 PUSH6 0x627A7A723058 KECCAK256 0xa6 0x4d POP 0xba PUSH29 0xA8023B64D00C49E38AFA498258FDC2E4A694EF1C57201E9A757EC10029 ", - "sourceMap": "753:342:0:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;923:49;;8:9:-1;5:2;;;30:1;27;20:12;5:2;923:49:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:100:-1;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;923:49:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1087:187:1;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1087:187:1;;;;;;;;;;;;;;;;;;;;;;;;;;;846:32:0;;8:9:-1;5:2;;;30:1;27;20:12;5:2;846:32:0;;;;;;;;;;;;;;;;;;;;1066:609:3;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1066:609:3;;;;;;;;;;;;;;805:35:0;;8:9:-1;5:2;;;30:1;27;20:12;5:2;805:35:0;;;;;;;;;;;;;;;;;;;;;;;982:99:1;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;982:99:1;;;;;;;978:37:0;;8:9:-1;5:2;;;30:1;27;20:12;5:2;978:37:0;;;;125:410:1;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;125:410:1;;;;;;;;;1280:126;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1280:126:1;;;;;;;;;;;;923:49:0;;;;;;;;;;;;;;;;;;;:::o;1087:187:1:-;1168:10;1144:4;1160:19;;;:7;:19;;;;;;;;;:29;;;;;;;;;;;:38;;;1208;;;;;;;1144:4;;1160:29;;1168:10;;1208:38;;;;;;;;-1:-1:-1;1263:4:1;1087:187;;;;;:::o;846:32:0:-;;;;:::o;1066:609:3:-;1198:14;;;1161:4;1198:14;;;:7;:14;;;;;;;;1213:10;1198:26;;;;;;;;1238:15;;;;;;;;;;1161:4;;1198:26;1238:25;-1:-1:-1;1238:25:3;;;:60;;;1292:6;1279:9;:19;;1238:60;:115;;;;-1:-1:-1;1340:13:3;;;:8;:13;;;;;;;;;;;1314:22;;;:39;;1238:115;1234:435;;;1378:13;;;;:8;:13;;;;;;;;;;;:23;;;;;;1415:15;;;;;;:25;;;;;;;768:10;1458:20;;1454:95;;;1498:14;;;;;;;:7;:14;;;;;;;;1513:10;1498:26;;;;;;;:36;;;;;;;1454:95;1578:3;1562:28;;1571:5;1562:28;;;1583:6;1562:28;;;;;;;;;;;;;;;;;;1611:4;1604:11;;;;1234:435;1653:5;1646:12;;1234:435;1066:609;;;;;;:::o;805:35:0:-;838:2;805:35;:::o;982:99:1:-;1058:16;;1035:4;1058:16;;;;;;;;;;;;982:99::o;978:37:0:-;;;;;;;;;;;;;;;;;;;:::o;125:410:1:-;276:10;178:4;267:20;;;;;;;;;;;:30;-1:-1:-1;267:30:1;;;:73;;-1:-1:-1;327:13:1;;;:8;:13;;;;;;;;;;;301:22;;;:39;;267:73;263:266;;;365:10;356:8;:20;;;;;;;;;;;:30;;;;;;;:20;400:13;;;;;;;;;:23;;;;;;437:33;;;;;;;400:13;;365:10;437:33;;;;;;;;;;;-1:-1:-1;491:4:1;484:11;;263:266;-1:-1:-1;521:5:1;514:12;;1280:126;1374:15;;;;1351:4;1374:15;;;:7;:15;;;;;;;;:25;;;;;;;;;;;;;1280:126::o" + "object": "0x6080604052600436106100985763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166306fdde03811461009d578063095ea7b31461012757806318160ddd1461016c57806323b872dd14610193578063313ce567146101ca57806370a08231146101f557806395d89b4114610223578063a9059cbb14610238578063dd62ed3e14610269575b600080fd5b3480156100a957600080fd5b506100b261029d565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100ec5781810151838201526020016100d4565b50505050905090810190601f1680156101195780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561013357600080fd5b5061015873ffffffffffffffffffffffffffffffffffffffff600435166024356102d4565b604080519115158252519081900360200190f35b34801561017857600080fd5b50610181610348565b60408051918252519081900360200190f35b34801561019f57600080fd5b5061015873ffffffffffffffffffffffffffffffffffffffff6004358116906024351660443561034e565b3480156101d657600080fd5b506101df6104e8565b6040805160ff9092168252519081900360200190f35b34801561020157600080fd5b5061018173ffffffffffffffffffffffffffffffffffffffff600435166104ed565b34801561022f57600080fd5b506100b2610515565b34801561024457600080fd5b5061015873ffffffffffffffffffffffffffffffffffffffff6004351660243561054c565b34801561027557600080fd5b5061018173ffffffffffffffffffffffffffffffffffffffff60043581169060243516610617565b60408051808201909152601181527f30782050726f746f636f6c20546f6b656e000000000000000000000000000000602082015281565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a35060015b92915050565b60035481565b73ffffffffffffffffffffffffffffffffffffffff8316600081815260016020908152604080832033845282528083205493835290829052812054909190831180159061039b5750828110155b80156103ce575073ffffffffffffffffffffffffffffffffffffffff841660009081526020819052604090205483810110155b156104db5773ffffffffffffffffffffffffffffffffffffffff808516600090815260208190526040808220805487019055918716815220805484900390557fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81101561046d5773ffffffffffffffffffffffffffffffffffffffff851660009081526001602090815260408083203384529091529020805484900390555b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a3600191506104e0565b600091505b509392505050565b601281565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b60408051808201909152600381527f5a52580000000000000000000000000000000000000000000000000000000000602082015281565b336000908152602081905260408120548211801590610592575073ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604090205482810110155b1561060f57336000818152602081815260408083208054879003905573ffffffffffffffffffffffffffffffffffffffff871680845292819020805487019055805186815290519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a3506001610342565b506000610342565b73ffffffffffffffffffffffffffffffffffffffff9182166000908152600160209081526040808320939094168252919091522054905600a165627a7a72305820d9d363d7eef4f71b2db22174233e9d08dc044f8bfd3ec76c93b896b38fefdf0b0029", + "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH2 0x98 JUMPI PUSH4 0xFFFFFFFF PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH1 0x0 CALLDATALOAD DIV AND PUSH4 0x6FDDE03 DUP2 EQ PUSH2 0x9D JUMPI DUP1 PUSH4 0x95EA7B3 EQ PUSH2 0x127 JUMPI DUP1 PUSH4 0x18160DDD EQ PUSH2 0x16C JUMPI DUP1 PUSH4 0x23B872DD EQ PUSH2 0x193 JUMPI DUP1 PUSH4 0x313CE567 EQ PUSH2 0x1CA JUMPI DUP1 PUSH4 0x70A08231 EQ PUSH2 0x1F5 JUMPI DUP1 PUSH4 0x95D89B41 EQ PUSH2 0x223 JUMPI DUP1 PUSH4 0xA9059CBB EQ PUSH2 0x238 JUMPI DUP1 PUSH4 0xDD62ED3E EQ PUSH2 0x269 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0xA9 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0xB2 PUSH2 0x29D JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 DUP1 DUP3 MSTORE DUP4 MLOAD DUP2 DUP4 ADD MSTORE DUP4 MLOAD SWAP2 SWAP3 DUP4 SWAP3 SWAP1 DUP4 ADD SWAP2 DUP6 ADD SWAP1 DUP1 DUP4 DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0xEC JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH2 0xD4 JUMP JUMPDEST POP POP POP POP SWAP1 POP SWAP1 DUP2 ADD SWAP1 PUSH1 0x1F AND DUP1 ISZERO PUSH2 0x119 JUMPI DUP1 DUP3 SUB DUP1 MLOAD PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP JUMPDEST POP SWAP3 POP POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x133 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x158 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD AND PUSH1 0x24 CALLDATALOAD PUSH2 0x2D4 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 ISZERO ISZERO DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x178 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x181 PUSH2 0x348 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x19F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x158 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD DUP2 AND SWAP1 PUSH1 0x24 CALLDATALOAD AND PUSH1 0x44 CALLDATALOAD PUSH2 0x34E JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1D6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1DF PUSH2 0x4E8 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0xFF SWAP1 SWAP3 AND DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x201 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x181 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD AND PUSH2 0x4ED JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x22F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0xB2 PUSH2 0x515 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x244 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x158 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD AND PUSH1 0x24 CALLDATALOAD PUSH2 0x54C JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x275 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x181 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD DUP2 AND SWAP1 PUSH1 0x24 CALLDATALOAD AND PUSH2 0x617 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP1 DUP3 ADD SWAP1 SWAP2 MSTORE PUSH1 0x11 DUP2 MSTORE PUSH32 0x30782050726F746F636F6C20546F6B656E000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE DUP2 JUMP JUMPDEST CALLER PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP8 AND DUP1 DUP6 MSTORE SWAP1 DUP4 MSTORE DUP2 DUP5 KECCAK256 DUP7 SWAP1 SSTORE DUP2 MLOAD DUP7 DUP2 MSTORE SWAP2 MLOAD SWAP4 SWAP5 SWAP1 SWAP4 SWAP1 SWAP3 PUSH32 0x8C5BE1E5EBEC7D5BD14F71427D1E84F3DD0314C0F7B2291E5B200AC8C7C3B925 SWAP3 DUP3 SWAP1 SUB ADD SWAP1 LOG3 POP PUSH1 0x1 JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x3 SLOAD DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 CALLER DUP5 MSTORE DUP3 MSTORE DUP1 DUP4 KECCAK256 SLOAD SWAP4 DUP4 MSTORE SWAP1 DUP3 SWAP1 MSTORE DUP2 KECCAK256 SLOAD SWAP1 SWAP2 SWAP1 DUP4 GT DUP1 ISZERO SWAP1 PUSH2 0x39B JUMPI POP DUP3 DUP2 LT ISZERO JUMPDEST DUP1 ISZERO PUSH2 0x3CE JUMPI POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD DUP4 DUP2 ADD LT ISZERO JUMPDEST ISZERO PUSH2 0x4DB JUMPI PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP6 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 DUP1 DUP3 KECCAK256 DUP1 SLOAD DUP8 ADD SWAP1 SSTORE SWAP2 DUP8 AND DUP2 MSTORE KECCAK256 DUP1 SLOAD DUP5 SWAP1 SUB SWAP1 SSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 LT ISZERO PUSH2 0x46D JUMPI PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP6 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 CALLER DUP5 MSTORE SWAP1 SWAP2 MSTORE SWAP1 KECCAK256 DUP1 SLOAD DUP5 SWAP1 SUB SWAP1 SSTORE JUMPDEST DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP6 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF DUP6 PUSH1 0x40 MLOAD DUP1 DUP3 DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG3 PUSH1 0x1 SWAP2 POP PUSH2 0x4E0 JUMP JUMPDEST PUSH1 0x0 SWAP2 POP JUMPDEST POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x12 DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD SWAP1 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP1 DUP3 ADD SWAP1 SWAP2 MSTORE PUSH1 0x3 DUP2 MSTORE PUSH32 0x5A52580000000000000000000000000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE DUP2 JUMP JUMPDEST CALLER PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 DUP2 KECCAK256 SLOAD DUP3 GT DUP1 ISZERO SWAP1 PUSH2 0x592 JUMPI POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD DUP3 DUP2 ADD LT ISZERO JUMPDEST ISZERO PUSH2 0x60F JUMPI CALLER PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x20 DUP2 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 DUP1 SLOAD DUP8 SWAP1 SUB SWAP1 SSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP8 AND DUP1 DUP5 MSTORE SWAP3 DUP2 SWAP1 KECCAK256 DUP1 SLOAD DUP8 ADD SWAP1 SSTORE DUP1 MLOAD DUP7 DUP2 MSTORE SWAP1 MLOAD SWAP3 SWAP4 SWAP3 PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF SWAP3 SWAP2 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG3 POP PUSH1 0x1 PUSH2 0x342 JUMP JUMPDEST POP PUSH1 0x0 PUSH2 0x342 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP2 DUP3 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP4 SWAP1 SWAP5 AND DUP3 MSTORE SWAP2 SWAP1 SWAP2 MSTORE KECCAK256 SLOAD SWAP1 JUMP STOP LOG1 PUSH6 0x627A7A723058 KECCAK256 0xd9 0xd3 PUSH4 0xD7EEF4F7 SHL 0x2d 0xb2 0x21 PUSH21 0x233E9D08DC044F8BFD3EC76C93B896B38FEFDF0B00 0x29 ", + "sourceMap": "750:342:3:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;920:49;;8:9:-1;5:2;;;30:1;27;20:12;5:2;920:49:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:100:-1;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;920:49:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1087:187:0;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1087:187:0;;;;;;;;;;;;;;;;;;;;;;;;;;;843:32:3;;8:9:-1;5:2;;;30:1;27;20:12;5:2;843:32:3;;;;;;;;;;;;;;;;;;;;1066:609:2;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1066:609:2;;;;;;;;;;;;;;802:35:3;;8:9:-1;5:2;;;30:1;27;20:12;5:2;802:35:3;;;;;;;;;;;;;;;;;;;;;;;982:99:0;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;982:99:0;;;;;;;975:37:3;;8:9:-1;5:2;;;30:1;27;20:12;5:2;975:37:3;;;;125:410:0;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;125:410:0;;;;;;;;;1280:126;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1280:126:0;;;;;;;;;;;;920:49:3;;;;;;;;;;;;;;;;;;;:::o;1087:187:0:-;1168:10;1144:4;1160:19;;;:7;:19;;;;;;;;;:29;;;;;;;;;;;:38;;;1208;;;;;;;1144:4;;1160:29;;1168:10;;1208:38;;;;;;;;-1:-1:-1;1263:4:0;1087:187;;;;;:::o;843:32:3:-;;;;:::o;1066:609:2:-;1198:14;;;1161:4;1198:14;;;:7;:14;;;;;;;;1213:10;1198:26;;;;;;;;1238:15;;;;;;;;;;1161:4;;1198:26;1238:25;-1:-1:-1;1238:25:2;;;:60;;;1292:6;1279:9;:19;;1238:60;:115;;;;-1:-1:-1;1340:13:2;;;:8;:13;;;;;;;;;;;1314:22;;;:39;;1238:115;1234:435;;;1378:13;;;;:8;:13;;;;;;;;;;;:23;;;;;;1415:15;;;;;;:25;;;;;;;768:10;1458:20;;1454:95;;;1498:14;;;;;;;:7;:14;;;;;;;;1513:10;1498:26;;;;;;;:36;;;;;;;1454:95;1578:3;1562:28;;1571:5;1562:28;;;1583:6;1562:28;;;;;;;;;;;;;;;;;;1611:4;1604:11;;;;1234:435;1653:5;1646:12;;1234:435;1066:609;;;;;;:::o;802:35:3:-;835:2;802:35;:::o;982:99:0:-;1058:16;;1035:4;1058:16;;;;;;;;;;;;982:99::o;975:37:3:-;;;;;;;;;;;;;;;;;;;:::o;125:410:0:-;276:10;178:4;267:20;;;;;;;;;;;:30;-1:-1:-1;267:30:0;;;:73;;-1:-1:-1;327:13:0;;;:8;:13;;;;;;;;;;;301:22;;;:39;;267:73;263:266;;;365:10;356:8;:20;;;;;;;;;;;:30;;;;;;;:20;400:13;;;;;;;;;:23;;;;;;437:33;;;;;;;400:13;;365:10;437:33;;;;;;;;;;;-1:-1:-1;491:4:0;484:11;;263:266;-1:-1:-1;521:5:0;514:12;;1280:126;1374:15;;;;1351:4;1374:15;;;:7;:15;;;;;;;;:25;;;;;;;;;;;;;1280:126::o" } } }, "sources": { - "current/tokens/ZRXToken/ZRXToken.sol": { + "1.0.0/ERC20Token/ERC20Token_v1.sol": { "id": 0 }, - "previous/ERC20Token/ERC20Token_v1.sol": { + "1.0.0/Token/Token_v1.sol": { "id": 1 }, - "previous/Token/Token_v1.sol": { + "1.0.0/UnlimitedAllowanceToken/UnlimitedAllowanceToken_v1.sol": { "id": 2 }, - "previous/UnlimitedAllowanceToken/UnlimitedAllowanceToken_v1.sol": { + "2.0.0/tokens/ZRXToken/ZRXToken.sol": { "id": 3 } }, "sourceCodes": { - "current/tokens/ZRXToken/ZRXToken.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity ^0.4.11;\n\nimport { UnlimitedAllowanceToken_v1 as UnlimitedAllowanceToken } from \"../../../previous/UnlimitedAllowanceToken/UnlimitedAllowanceToken_v1.sol\";\n\ncontract ZRXToken is UnlimitedAllowanceToken {\n\n uint8 constant public decimals = 18;\n uint public totalSupply = 10**27; // 1 billion tokens, 18 decimal places\n string constant public name = \"0x Protocol Token\";\n string constant public symbol = \"ZRX\";\n\n function ZRXToken() {\n balances[msg.sender] = totalSupply;\n }\n}\n", - "previous/ERC20Token/ERC20Token_v1.sol": "pragma solidity ^0.4.11;\n\nimport { Token_v1 as Token } from \"../Token/Token_v1.sol\";\n\ncontract ERC20Token_v1 is Token {\n\n function transfer(address _to, uint _value) returns (bool) {\n //Default assumes totalSupply can't be over max (2^256 - 1).\n if (balances[msg.sender] >= _value && balances[_to] + _value >= balances[_to]) {\n balances[msg.sender] -= _value;\n balances[_to] += _value;\n Transfer(msg.sender, _to, _value);\n return true;\n } else { return false; }\n }\n\n function transferFrom(address _from, address _to, uint _value) returns (bool) {\n if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value >= balances[_to]) {\n balances[_to] += _value;\n balances[_from] -= _value;\n allowed[_from][msg.sender] -= _value;\n Transfer(_from, _to, _value);\n return true;\n } else { return false; }\n }\n\n function balanceOf(address _owner) constant returns (uint) {\n return balances[_owner];\n }\n\n function approve(address _spender, uint _value) returns (bool) {\n allowed[msg.sender][_spender] = _value;\n Approval(msg.sender, _spender, _value);\n return true;\n }\n\n function allowance(address _owner, address _spender) constant returns (uint) {\n return allowed[_owner][_spender];\n }\n\n mapping (address => uint) balances;\n mapping (address => mapping (address => uint)) allowed;\n uint public totalSupply;\n}\n", - "previous/Token/Token_v1.sol": "pragma solidity ^0.4.11;\n\ncontract Token_v1 {\n\n /// @return total amount of tokens\n function totalSupply() constant returns (uint supply) {}\n\n /// @param _owner The address from which the balance will be retrieved\n /// @return The balance\n function balanceOf(address _owner) constant returns (uint balance) {}\n\n /// @notice send `_value` token to `_to` from `msg.sender`\n /// @param _to The address of the recipient\n /// @param _value The amount of token to be transferred\n /// @return Whether the transfer was successful or not\n function transfer(address _to, uint _value) returns (bool success) {}\n\n /// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`\n /// @param _from The address of the sender\n /// @param _to The address of the recipient\n /// @param _value The amount of token to be transferred\n /// @return Whether the transfer was successful or not\n function transferFrom(address _from, address _to, uint _value) returns (bool success) {}\n\n /// @notice `msg.sender` approves `_addr` to spend `_value` tokens\n /// @param _spender The address of the account able to transfer the tokens\n /// @param _value The amount of wei to be approved for transfer\n /// @return Whether the approval was successful or not\n function approve(address _spender, uint _value) returns (bool success) {}\n\n /// @param _owner The address of the account owning tokens\n /// @param _spender The address of the account able to transfer the tokens\n /// @return Amount of remaining tokens allowed to spent\n function allowance(address _owner, address _spender) constant returns (uint remaining) {}\n\n event Transfer(address indexed _from, address indexed _to, uint _value);\n event Approval(address indexed _owner, address indexed _spender, uint _value);\n}\n\n", - "previous/UnlimitedAllowanceToken/UnlimitedAllowanceToken_v1.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity ^0.4.11;\n\nimport { ERC20Token_v1 as ERC20Token } from \"../ERC20Token/ERC20Token_v1.sol\";\n\ncontract UnlimitedAllowanceToken_v1 is ERC20Token {\n\n uint constant MAX_UINT = 2**256 - 1;\n\n /// @dev ERC20 transferFrom, modified such that an allowance of MAX_UINT represents an unlimited allowance.\n /// @param _from Address to transfer from.\n /// @param _to Address to transfer to.\n /// @param _value Amount to transfer.\n /// @return Success of transfer.\n function transferFrom(address _from, address _to, uint _value)\n public\n returns (bool)\n {\n uint allowance = allowed[_from][msg.sender];\n if (balances[_from] >= _value\n && allowance >= _value\n && balances[_to] + _value >= balances[_to]\n ) {\n balances[_to] += _value;\n balances[_from] -= _value;\n if (allowance < MAX_UINT) {\n allowed[_from][msg.sender] -= _value;\n }\n Transfer(_from, _to, _value);\n return true;\n } else {\n return false;\n }\n }\n}\n" + "1.0.0/ERC20Token/ERC20Token_v1.sol": "pragma solidity ^0.4.11;\n\nimport { Token_v1 as Token } from \"../Token/Token_v1.sol\";\n\ncontract ERC20Token_v1 is Token {\n\n function transfer(address _to, uint _value) returns (bool) {\n //Default assumes totalSupply can't be over max (2^256 - 1).\n if (balances[msg.sender] >= _value && balances[_to] + _value >= balances[_to]) {\n balances[msg.sender] -= _value;\n balances[_to] += _value;\n Transfer(msg.sender, _to, _value);\n return true;\n } else { return false; }\n }\n\n function transferFrom(address _from, address _to, uint _value) returns (bool) {\n if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value >= balances[_to]) {\n balances[_to] += _value;\n balances[_from] -= _value;\n allowed[_from][msg.sender] -= _value;\n Transfer(_from, _to, _value);\n return true;\n } else { return false; }\n }\n\n function balanceOf(address _owner) constant returns (uint) {\n return balances[_owner];\n }\n\n function approve(address _spender, uint _value) returns (bool) {\n allowed[msg.sender][_spender] = _value;\n Approval(msg.sender, _spender, _value);\n return true;\n }\n\n function allowance(address _owner, address _spender) constant returns (uint) {\n return allowed[_owner][_spender];\n }\n\n mapping (address => uint) balances;\n mapping (address => mapping (address => uint)) allowed;\n uint public totalSupply;\n}\n", + "1.0.0/Token/Token_v1.sol": "pragma solidity ^0.4.11;\n\ncontract Token_v1 {\n\n /// @return total amount of tokens\n function totalSupply() constant returns (uint supply) {}\n\n /// @param _owner The address from which the balance will be retrieved\n /// @return The balance\n function balanceOf(address _owner) constant returns (uint balance) {}\n\n /// @notice send `_value` token to `_to` from `msg.sender`\n /// @param _to The address of the recipient\n /// @param _value The amount of token to be transferred\n /// @return Whether the transfer was successful or not\n function transfer(address _to, uint _value) returns (bool success) {}\n\n /// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`\n /// @param _from The address of the sender\n /// @param _to The address of the recipient\n /// @param _value The amount of token to be transferred\n /// @return Whether the transfer was successful or not\n function transferFrom(address _from, address _to, uint _value) returns (bool success) {}\n\n /// @notice `msg.sender` approves `_addr` to spend `_value` tokens\n /// @param _spender The address of the account able to transfer the tokens\n /// @param _value The amount of wei to be approved for transfer\n /// @return Whether the approval was successful or not\n function approve(address _spender, uint _value) returns (bool success) {}\n\n /// @param _owner The address of the account owning tokens\n /// @param _spender The address of the account able to transfer the tokens\n /// @return Amount of remaining tokens allowed to spent\n function allowance(address _owner, address _spender) constant returns (uint remaining) {}\n\n event Transfer(address indexed _from, address indexed _to, uint _value);\n event Approval(address indexed _owner, address indexed _spender, uint _value);\n}\n\n", + "1.0.0/UnlimitedAllowanceToken/UnlimitedAllowanceToken_v1.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity ^0.4.11;\n\nimport { ERC20Token_v1 as ERC20Token } from \"../ERC20Token/ERC20Token_v1.sol\";\n\ncontract UnlimitedAllowanceToken_v1 is ERC20Token {\n\n uint constant MAX_UINT = 2**256 - 1;\n\n /// @dev ERC20 transferFrom, modified such that an allowance of MAX_UINT represents an unlimited allowance.\n /// @param _from Address to transfer from.\n /// @param _to Address to transfer to.\n /// @param _value Amount to transfer.\n /// @return Success of transfer.\n function transferFrom(address _from, address _to, uint _value)\n public\n returns (bool)\n {\n uint allowance = allowed[_from][msg.sender];\n if (balances[_from] >= _value\n && allowance >= _value\n && balances[_to] + _value >= balances[_to]\n ) {\n balances[_to] += _value;\n balances[_from] -= _value;\n if (allowance < MAX_UINT) {\n allowed[_from][msg.sender] -= _value;\n }\n Transfer(_from, _to, _value);\n return true;\n } else {\n return false;\n }\n }\n}\n", + "2.0.0/tokens/ZRXToken/ZRXToken.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity ^0.4.11;\n\nimport { UnlimitedAllowanceToken_v1 as UnlimitedAllowanceToken } from \"../../../1.0.0/UnlimitedAllowanceToken/UnlimitedAllowanceToken_v1.sol\";\n\ncontract ZRXToken is UnlimitedAllowanceToken {\n\n uint8 constant public decimals = 18;\n uint public totalSupply = 10**27; // 1 billion tokens, 18 decimal places\n string constant public name = \"0x Protocol Token\";\n string constant public symbol = \"ZRX\";\n\n function ZRXToken() {\n balances[msg.sender] = totalSupply;\n }\n}\n" }, - "sourceTreeHashHex": "0xbaaa4c45ee7e505106ec3f265920d9be632c2dec05ba8ea8e7e7404805ee62a5", + "sourceTreeHashHex": "0xde47979523dda114901886ad7b4f9f43f0f3ccf10556e2b56d29370f0bc74d1d", "compiler": { "name": "solc", "version": "soljson-v0.4.24+commit.e67f0147.js", @@ -283,4 +283,4 @@ } }, "networks": {} -} +} \ No newline at end of file diff --git a/packages/migrations/package.json b/packages/migrations/package.json index 57bf0875a6..a5fd3756ac 100644 --- a/packages/migrations/package.json +++ b/packages/migrations/package.json @@ -67,10 +67,10 @@ "@0xproject/utils": "^0.7.1", "@0xproject/web3-wrapper": "^0.7.1", "@ledgerhq/hw-app-eth": "^4.3.0", - "ethereum-types": "^0.0.1", + "ethereum-types": "^0.0.2", "ethers": "3.0.22", "lodash": "^4.17.4", - "web3-provider-engine": "^14.0.4" + "web3-provider-engine": "14.0.6" }, "optionalDependencies": { "@ledgerhq/hw-transport-node-hid": "^4.3.0" diff --git a/packages/monorepo-scripts/CHANGELOG.json b/packages/monorepo-scripts/CHANGELOG.json index a1dda01764..781704a8ed 100644 --- a/packages/monorepo-scripts/CHANGELOG.json +++ b/packages/monorepo-scripts/CHANGELOG.json @@ -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, "version": "0.2.1", diff --git a/packages/monorepo-scripts/src/prepublish_checks.ts b/packages/monorepo-scripts/src/prepublish_checks.ts index 1c4ee1fc6f..3b4ff9082e 100644 --- a/packages/monorepo-scripts/src/prepublish_checks.ts +++ b/packages/monorepo-scripts/src/prepublish_checks.ts @@ -34,17 +34,17 @@ async function checkGitTagsForNextVersionAndDeleteIfExistAsync( const packageLocation = lernaPackage.location; 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]; if (_.includes(remoteTagVersions, nextVersion)) { const tagName = `:refs/tags/${packageName}@${nextVersion}`; await utils.removeRemoteTagAsync(tagName); } + + const localTagVersions = localTagVersionsByPackageName[packageName]; + if (_.includes(localTagVersions, nextVersion)) { + const tagName = `${packageName}@${nextVersion}`; + await utils.removeLocalTagAsync(tagName); + } } } diff --git a/packages/monorepo-scripts/src/utils/utils.ts b/packages/monorepo-scripts/src/utils/utils.ts index 20bc57bae5..f819ab6f11 100644 --- a/packages/monorepo-scripts/src/utils/utils.ts +++ b/packages/monorepo-scripts/src/utils/utils.ts @@ -44,6 +44,9 @@ export const utils = { nextVersionIfValid = semver.inc(currentVersion, 'patch'); } 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) ? semver.inc(currentVersion, 'patch') : lastEntry.version; @@ -100,19 +103,23 @@ export const utils = { return tagVersionByPackageName; }, async removeLocalTagAsync(tagName: string): Promise { - const result = 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}`); + try { + await execAsync(`git tag -d ${tagName}`, { + cwd: constants.monorepoRootPath, + }); + } 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 { - const result = 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}`); + try { + await execAsync(`git push origin ${tagName}`, { + cwd: constants.monorepoRootPath, + }); + } catch (err) { + throw new Error(`Failed to delete remote git tag. Got err: ${err}`); } + this.log(`Removed remote tag: ${tagName}`); }, }; diff --git a/packages/order-utils/CHANGELOG.json b/packages/order-utils/CHANGELOG.json index a1f1997935..199ce21731 100644 --- a/packages/order-utils/CHANGELOG.json +++ b/packages/order-utils/CHANGELOG.json @@ -1,7 +1,11 @@ [ { - "version": "0.1.0", + "version": "1.0.0", "changes": [ + { + "note": "Refactor to work with V2 of 0x protocol", + "pr": 636 + }, { "note": "Export parseECSignature method", "pr": 684 diff --git a/packages/order-utils/package.json b/packages/order-utils/package.json index e3c81de449..97b08c3d38 100644 --- a/packages/order-utils/package.json +++ b/packages/order-utils/package.json @@ -82,7 +82,7 @@ "@0xproject/web3-wrapper": "^0.7.1", "@types/node": "^8.0.53", "bn.js": "^4.11.8", - "ethereum-types": "^0.0.1", + "ethereum-types": "^0.0.2", "ethereumjs-abi": "^0.6.4", "ethereumjs-util": "^5.1.1", "ethers": "3.0.22", diff --git a/packages/order-watcher/package.json b/packages/order-watcher/package.json index 3335713a34..40613ea967 100644 --- a/packages/order-watcher/package.json +++ b/packages/order-watcher/package.json @@ -85,9 +85,9 @@ "@0xproject/order-utils": "^0.0.7", "@0xproject/types": "^0.8.1", "@0xproject/typescript-typings": "^0.4.1", - "@0xproject/utils": "^0.7.3", + "@0xproject/utils": "^0.7.2", "@0xproject/web3-wrapper": "^0.7.1", - "ethereum-types": "^0.0.1", + "ethereum-types": "^0.0.2", "bintrees": "^1.0.2", "ethers": "3.0.22", "lodash": "^4.17.4" diff --git a/packages/order-watcher/src/order_watcher/order_watcher.ts b/packages/order-watcher/src/order_watcher/order_watcher.ts index 140aa341bc..0ee56592ec 100644 --- a/packages/order-watcher/src/order_watcher/order_watcher.ts +++ b/packages/order-watcher/src/order_watcher/order_watcher.ts @@ -1,7 +1,10 @@ import { BalanceAndProxyAllowanceLazyStore, ContractWrappers, + LogCancelContractEventArgs, + LogFillContractEventArgs, OrderFilledCancelledLazyStore, + WithdrawalContractEventArgs, } from '@0xproject/contract-wrappers'; import { schemas } from '@0xproject/json-schemas'; import { getOrderHashHex, OrderStateUtils } from '@0xproject/order-utils'; @@ -23,14 +26,8 @@ import { DepositContractEventArgs, EtherTokenContractEventArgs, EtherTokenEvents, - WithdrawalContractEventArgs, } from '../generated_contract_wrappers/ether_token'; -import { - ExchangeContractEventArgs, - ExchangeEvents, - LogCancelContractEventArgs, - LogFillContractEventArgs, -} from '../generated_contract_wrappers/exchange'; +import { ExchangeContractEventArgs, ExchangeEvents } from '../generated_contract_wrappers/exchange'; import { ApprovalContractEventArgs, TokenContractEventArgs, @@ -301,6 +298,7 @@ export class OrderWatcher { } case EtherTokenEvents.Withdrawal: { // Invalidate cache + // tslint:disable-next-line:no-unnecessary-type-assertion const args = decodedLog.args as WithdrawalContractEventArgs; this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._owner); // Revalidate orders @@ -317,6 +315,7 @@ export class OrderWatcher { } case ExchangeEvents.LogFill: { // Invalidate cache + // tslint:disable-next-line:no-unnecessary-type-assertion const args = decodedLog.args as LogFillContractEventArgs; this._orderFilledCancelledLazyStore.deleteFilledTakerAmount(args.orderHash); // Revalidate orders @@ -329,6 +328,7 @@ export class OrderWatcher { } case ExchangeEvents.LogCancel: { // Invalidate cache + // tslint:disable-next-line:no-unnecessary-type-assertion const args = decodedLog.args as LogCancelContractEventArgs; this._orderFilledCancelledLazyStore.deleteCancelledTakerAmount(args.orderHash); // Revalidate orders diff --git a/packages/sol-compiler/package.json b/packages/sol-compiler/package.json index 98568c2706..d5a2e6e774 100644 --- a/packages/sol-compiler/package.json +++ b/packages/sol-compiler/package.json @@ -84,7 +84,7 @@ "@0xproject/utils": "^0.7.1", "@0xproject/web3-wrapper": "^0.7.1", "@types/yargs": "^11.0.0", - "ethereum-types": "^0.0.1", + "ethereum-types": "^0.0.2", "chalk": "^2.3.0", "ethereumjs-util": "^5.1.1", "isomorphic-fetch": "^2.2.1", @@ -93,7 +93,6 @@ "require-from-string": "^2.0.1", "semver": "^5.5.0", "solc": "^0.4.23", - "web3": "^0.20.0", "web3-eth-abi": "^1.0.0-beta.24", "yargs": "^10.0.3" }, diff --git a/packages/sol-cov/package.json b/packages/sol-cov/package.json index 3d1b7f9002..483500ca95 100644 --- a/packages/sol-cov/package.json +++ b/packages/sol-cov/package.json @@ -56,7 +56,7 @@ "@0xproject/utils": "^0.7.1", "@0xproject/web3-wrapper": "^0.7.1", "@0xproject/dev-utils": "^0.4.4", - "ethereum-types": "^0.0.1", + "ethereum-types": "^0.0.2", "ethereumjs-util": "^5.1.1", "glob": "^7.1.2", "istanbul": "^0.4.5", diff --git a/packages/subproviders/CHANGELOG.json b/packages/subproviders/CHANGELOG.json index 65a74f7a87..c4dca28645 100644 --- a/packages/subproviders/CHANGELOG.json +++ b/packages/subproviders/CHANGELOG.json @@ -1,4 +1,12 @@ [ + { + "version": "0.11.0", + "changes": [ + { + "note": "Add `EthLightwalletSubprovider`" + } + ] + }, { "timestamp": 1529397769, "version": "0.10.4", diff --git a/packages/subproviders/package.json b/packages/subproviders/package.json index 31b6ffed64..8eca91dc70 100644 --- a/packages/subproviders/package.json +++ b/packages/subproviders/package.json @@ -41,27 +41,27 @@ }, "dependencies": { "@0xproject/assert": "^0.2.12", - "@0xproject/types": "^0.8.1", - "@0xproject/typescript-typings": "^0.4.1", + "@0xproject/types": "^1.0.0", + "@0xproject/web3-wrapper": "^0.7.1", + "@0xproject/typescript-typings": "^0.4.2", "@0xproject/utils": "^0.7.1", "@ledgerhq/hw-app-eth": "^4.3.0", "@ledgerhq/hw-transport-u2f": "^4.3.0", - "ethereum-types": "^0.0.1", + "ethereum-types": "^0.0.2", "bip39": "^2.5.0", "bn.js": "^4.11.8", + "eth-lightwallet": "^3.0.1", "ethereumjs-tx": "^1.3.5", "ethereumjs-util": "^5.1.1", "ganache-core": "0xProject/ganache-core", "hdkey": "^0.7.1", "lodash": "^4.17.4", "semaphore-async-await": "^1.5.1", - "web3": "^0.20.0", - "web3-provider-engine": "^14.0.4" + "web3-provider-engine": "14.0.6" }, "devDependencies": { "@0xproject/monorepo-scripts": "^0.2.1", "@0xproject/tslint-config": "^0.4.20", - "@0xproject/typescript-typings": "^0.4.2", "@0xproject/utils": "^0.7.1", "@types/bip39": "^2.4.0", "@types/bn.js": "^4.11.0", @@ -70,6 +70,7 @@ "@types/lodash": "4.14.104", "@types/mocha": "^2.2.42", "@types/node": "^8.0.53", + "@types/sinon": "^2.2.2", "chai": "^4.0.1", "chai-as-promised": "^7.1.0", "copyfiles": "^1.2.0", @@ -79,6 +80,7 @@ "npm-run-all": "^4.1.2", "nyc": "^11.0.1", "shx": "^0.2.2", + "sinon": "^4.0.0", "tslint": "5.8.0", "typedoc": "0xProject/typedoc", "typescript": "2.7.1", diff --git a/packages/subproviders/src/globals.d.ts b/packages/subproviders/src/globals.d.ts index c00dc40997..94e63a32de 100644 --- a/packages/subproviders/src/globals.d.ts +++ b/packages/subproviders/src/globals.d.ts @@ -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' { const json: any; /* tslint:disable */ diff --git a/packages/subproviders/src/index.ts b/packages/subproviders/src/index.ts index 9194c1341b..71d643f148 100644 --- a/packages/subproviders/src/index.ts +++ b/packages/subproviders/src/index.ts @@ -7,7 +7,7 @@ import { LedgerEthereumClient } from './types'; export { prependSubprovider } from './utils/subprovider_utils'; export { EmptyWalletSubprovider } from './subproviders/empty_wallet_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 { LedgerSubprovider } from './subproviders/ledger'; export { GanacheSubprovider } from './subproviders/ganache'; @@ -15,6 +15,7 @@ export { Subprovider } from './subproviders/subprovider'; export { NonceTrackerSubprovider } from './subproviders/nonce_tracker'; export { PrivateKeyWalletSubprovider } from './subproviders/private_key_wallet'; export { MnemonicWalletSubprovider } from './subproviders/mnemonic_wallet'; +export { EthLightwalletSubprovider } from './subproviders/eth_lightwallet_subprovider'; export { Callback, ErrorCallback, diff --git a/packages/subproviders/src/subproviders/eth_lightwallet_subprovider.ts b/packages/subproviders/src/subproviders/eth_lightwallet_subprovider.ts new file mode 100644 index 0000000000..a9ebbb790f --- /dev/null +++ b/packages/subproviders/src/subproviders/eth_lightwallet_subprovider.ts @@ -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 { + 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 { + // 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 { + let privKey = this._keystore.exportPrivateKey(address, this._pwDerivedKey); + const privKeyWallet = new PrivateKeyWalletSubprovider(privKey); + privKey = ''; + const result = privKeyWallet.signPersonalMessageAsync(data, address); + return result; + } +} diff --git a/packages/subproviders/src/subproviders/injected_web3.ts b/packages/subproviders/src/subproviders/signer.ts similarity index 55% rename from packages/subproviders/src/subproviders/injected_web3.ts rename to packages/subproviders/src/subproviders/signer.ts index 2691dec536..08a9dacebf 100644 --- a/packages/subproviders/src/subproviders/injected_web3.ts +++ b/packages/subproviders/src/subproviders/signer.ts @@ -1,5 +1,5 @@ +import { Web3Wrapper } from '@0xproject/web3-wrapper'; import { JSONRPCRequestPayload, Provider } from 'ethereum-types'; -import * as Web3 from 'web3'; 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) - * subprovider interface. It forwards JSON RPC requests involving user accounts (getAccounts, - * sendTransaction, etc...) to the provider instance supplied at instantiation. All other requests + * subprovider interface. It forwards JSON RPC requests involving the domain of a signer (getAccounts, + * sendTransaction, signMessage etc...) to the provider instance supplied at instantiation. All other requests * are passed onwards for subsequent subproviders to handle. */ -export class InjectedWeb3Subprovider extends Subprovider { - private _injectedWeb3: Web3; +export class SignerSubprovider extends Subprovider { + private _web3Wrapper: Web3Wrapper; /** - * Instantiates a new InjectedWeb3Subprovider + * Instantiates a new SignerSubprovider * @param provider Web3 provider that should handle all user account related requests */ constructor(provider: Provider) { super(); - this._injectedWeb3 = new Web3(provider); + this._web3Wrapper = new Web3Wrapper(provider); } /** * 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 { switch (payload.method) { case 'web3_clientVersion': - this._injectedWeb3.version.getNode(end); + try { + const nodeVersion = await this._web3Wrapper.getNodeVersionAsync(); + end(null, nodeVersion); + } catch (err) { + end(err); + } return; case 'eth_accounts': - this._injectedWeb3.eth.getAccounts(end); + try { + const accounts = await this._web3Wrapper.getAvailableAddressesAsync(); + end(null, accounts); + } catch (err) { + end(err); + } return; - case 'eth_sendTransaction': 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; - case 'eth_sign': 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; - default: next(); return; diff --git a/packages/subproviders/test/unit/eth_lightwallet_subprovider_test.ts b/packages/subproviders/test/unit/eth_lightwallet_subprovider_test.ts new file mode 100644 index 0000000000..f17c21f023 --- /dev/null +++ b/packages/subproviders/test/unit/eth_lightwallet_subprovider_test.ts @@ -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(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(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); + }); + }); + }); +}); diff --git a/packages/subproviders/test/unit/private_key_wallet_subprovider_test.ts b/packages/subproviders/test/unit/private_key_wallet_subprovider_test.ts index a41ad77900..ab321bcff7 100644 --- a/packages/subproviders/test/unit/private_key_wallet_subprovider_test.ts +++ b/packages/subproviders/test/unit/private_key_wallet_subprovider_test.ts @@ -60,6 +60,20 @@ describe('PrivateKeyWalletSubprovider', () => { }); 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) => { const messageHex = ethUtils.bufferToHex(ethUtils.toBuffer(fixtureData.PERSONAL_MESSAGE_STRING)); const payload = { diff --git a/packages/subproviders/test/unit/redundant_rpc_subprovider_test.ts b/packages/subproviders/test/unit/redundant_rpc_subprovider_test.ts index 593027849c..810fb8f455 100644 --- a/packages/subproviders/test/unit/redundant_rpc_subprovider_test.ts +++ b/packages/subproviders/test/unit/redundant_rpc_subprovider_test.ts @@ -1,6 +1,7 @@ import { DoneCallback } from '@0xproject/types'; import * as chai from 'chai'; import { JSONRPCResponsePayload } from 'ethereum-types'; +import * as Sinon from 'sinon'; import Web3ProviderEngine = require('web3-provider-engine'); import RpcSubprovider = require('web3-provider-engine/subproviders/rpc'); @@ -41,6 +42,9 @@ describe('RedundantSubprovider', () => { const nonExistentSubprovider = new RpcSubprovider({ rpcUrl: 'http://does-not-exist:3000', }); + const handleRequestStub = Sinon.stub(nonExistentSubprovider, 'handleRequest').throws( + new Error('REQUEST_FAILED'), + ); const subproviders = [nonExistentSubprovider as Subprovider, ganacheSubprovider]; const redundantSubprovider = new RedundantSubprovider(subproviders); provider.addProvider(redundantSubprovider); @@ -55,6 +59,7 @@ describe('RedundantSubprovider', () => { const callback = reportCallbackErrors(done)((err: Error, response: JSONRPCResponsePayload) => { expect(err).to.be.a('null'); expect(response.result.length).to.be.equal(DEFAULT_NUM_ACCOUNTS); + handleRequestStub.restore(); done(); }); provider.sendAsync(payload, callback); diff --git a/packages/testnet-faucets/package.json b/packages/testnet-faucets/package.json index 712ac67958..5acd4ae352 100644 --- a/packages/testnet-faucets/package.json +++ b/packages/testnet-faucets/package.json @@ -20,6 +20,7 @@ "dependencies": { "0x.js": "^0.38.0", "@0xproject/subproviders": "^0.10.4", + "@0xproject/web3-wrapper": "^0.7.1", "@0xproject/typescript-typings": "^0.4.1", "@0xproject/utils": "^0.7.1", "body-parser": "^1.17.1", @@ -28,15 +29,14 @@ "express": "^4.15.2", "lodash": "^4.17.4", "rollbar": "^0.6.5", - "web3": "^0.20.0", - "web3-provider-engine": "^14.0.4" + "web3-provider-engine": "14.0.6" }, "devDependencies": { "@0xproject/tslint-config": "^0.4.20", "@types/body-parser": "^1.16.1", "@types/express": "^4.0.35", "@types/lodash": "4.14.104", - "ethereum-types": "^0.0.1", + "ethereum-types": "^0.0.2", "awesome-typescript-loader": "^3.1.3", "gulp": "^3.9.1", "make-promises-safe": "^1.1.0", diff --git a/packages/testnet-faucets/src/ts/dispense_asset_tasks.ts b/packages/testnet-faucets/src/ts/dispense_asset_tasks.ts index 41b6c90cda..3af5ca747f 100644 --- a/packages/testnet-faucets/src/ts/dispense_asset_tasks.ts +++ b/packages/testnet-faucets/src/ts/dispense_asset_tasks.ts @@ -1,7 +1,7 @@ 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 Web3 from 'web3'; import { configs } from './configs'; @@ -13,22 +13,21 @@ const DISPENSE_MAX_AMOUNT_ETHER = 2; type AsyncTask = () => Promise; export const dispenseAssetTasks = { - dispenseEtherTask(recipientAddress: string, web3: Web3): AsyncTask { + dispenseEtherTask(recipientAddress: string, web3Wrapper: Web3Wrapper): AsyncTask { return async () => { logUtils.log(`Processing ETH ${recipientAddress}`); - const userBalance = await promisify(web3.eth.getBalance)(recipientAddress); - const maxAmountInWei = new BigNumber(web3.toWei(DISPENSE_MAX_AMOUNT_ETHER, 'ether')); + const userBalance = await web3Wrapper.getBalanceInWeiAsync(recipientAddress); + const maxAmountInWei = Web3Wrapper.toWei(new BigNumber(DISPENSE_MAX_AMOUNT_ETHER)); if (userBalance.greaterThanOrEqualTo(maxAmountInWei)) { logUtils.log( `User exceeded ETH balance maximum (${maxAmountInWei}) ${recipientAddress} ${userBalance} `, ); return; } - const sendTransactionAsync = promisify(web3.eth.sendTransaction); - const txHash = await sendTransactionAsync({ + const txHash = await web3Wrapper.sendTransactionAsync({ from: configs.DISPENSER_ADDRESS, 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}`); }; diff --git a/packages/testnet-faucets/src/ts/handler.ts b/packages/testnet-faucets/src/ts/handler.ts index 3a60d396c1..6d26691d6a 100644 --- a/packages/testnet-faucets/src/ts/handler.ts +++ b/packages/testnet-faucets/src/ts/handler.ts @@ -1,15 +1,10 @@ import { Order, ZeroEx } from '0x.js'; import { BigNumber, logUtils } from '@0xproject/utils'; +import { Web3Wrapper } from '@0xproject/web3-wrapper'; import { Provider } from 'ethereum-types'; import * as express from 'express'; 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 ProviderEngine = require('web3-provider-engine'); import RpcSubprovider = require('web3-provider-engine/subproviders/rpc'); @@ -22,7 +17,7 @@ import { rpcUrls } from './rpc_urls'; interface NetworkConfig { dispatchQueue: DispatchQueue; - web3: Web3; + web3Wrapper: Web3Wrapper; zeroEx: ZeroEx; } @@ -58,15 +53,15 @@ export class Handler { constructor() { _.forIn(rpcUrls, (rpcUrl: string, networkId: string) => { const providerObj = Handler._createProviderEngine(rpcUrl); - const web3 = new Web3(providerObj); + const web3Wrapper = new Web3Wrapper(providerObj); const zeroExConfig = { networkId: +networkId, }; - const zeroEx = new ZeroEx(web3.currentProvider, zeroExConfig); + const zeroEx = new ZeroEx(providerObj, zeroExConfig); const dispatchQueue = new DispatchQueue(); this._networkConfigByNetworkId[networkId] = { dispatchQueue, - web3, + web3Wrapper, zeroEx, }; }); @@ -106,7 +101,7 @@ export class Handler { let dispenserTask; switch (requestedAssetType) { case RequestedAssetType.ETH: - dispenserTask = dispenseAssetTasks.dispenseEtherTask(recipient, networkConfig.web3); + dispenserTask = dispenseAssetTasks.dispenseEtherTask(recipient, networkConfig.web3Wrapper); break; case RequestedAssetType.WETH: case RequestedAssetType.ZRX: diff --git a/packages/types/CHANGELOG.json b/packages/types/CHANGELOG.json index 40197ab927..46e67c9701 100644 --- a/packages/types/CHANGELOG.json +++ b/packages/types/CHANGELOG.json @@ -1,4 +1,15 @@ [ + { + "version": "1.0.0", + "changes": [ + { + "notes": "Updated types for V2 of 0x protocol" + }, + { + "note": "Add `ECSignatureBuffer`" + } + ] + }, { "timestamp": 1529397769, "version": "0.8.1", diff --git a/packages/types/package.json b/packages/types/package.json index 0d5e308b99..f79b558fa3 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -35,7 +35,7 @@ "dependencies": { "@types/node": "^8.0.53", "bignumber.js": "~4.1.0", - "ethereum-types": "^0.0.1" + "ethereum-types": "^0.0.2" }, "publishConfig": { "access": "public" diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 4ad10fdee3..9514320dab 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -48,6 +48,12 @@ export interface ECSignature { s: string; } +export interface ECSignatureBuffer { + v: number; + r: Buffer; + s: Buffer; +} + /** * Validator signature components */ diff --git a/packages/typescript-typings/CHANGELOG.json b/packages/typescript-typings/CHANGELOG.json index 1f397e6226..1d4230783c 100644 --- a/packages/typescript-typings/CHANGELOG.json +++ b/packages/typescript-typings/CHANGELOG.json @@ -2,6 +2,10 @@ { "version": "0.4.2", "changes": [ + { + "note": "Add types for `eth-lightwallet`", + "pr": 775 + }, { "note": "Improve 'web3-provider-engine' typings", "pr": 768 diff --git a/packages/typescript-typings/package.json b/packages/typescript-typings/package.json index 3ba7c10baa..e3f2262946 100644 --- a/packages/typescript-typings/package.json +++ b/packages/typescript-typings/package.json @@ -25,7 +25,7 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/typescript-typings#readme", "dependencies": { - "ethereum-types": "^0.0.1", + "ethereum-types": "^0.0.2", "bignumber.js": "~4.1.0" }, "devDependencies": { diff --git a/packages/typescript-typings/types/eth-lightwallet/index.d.ts b/packages/typescript-typings/types/eth-lightwallet/index.d.ts new file mode 100644 index 0000000000..58096e9f49 --- /dev/null +++ b/packages/typescript-typings/types/eth-lightwallet/index.d.ts @@ -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; + } +} diff --git a/packages/utils/CHANGELOG.json b/packages/utils/CHANGELOG.json index c6736dc4db..a27216be73 100644 --- a/packages/utils/CHANGELOG.json +++ b/packages/utils/CHANGELOG.json @@ -1,13 +1,4 @@ [ - { - "version": "0.7.3", - "changes": [ - { - "note": "Fixes uncaught Error in abi_decoder", - "pr": 763 - } - ] - }, { "version": "0.7.2", "changes": [ @@ -17,6 +8,10 @@ { "note": "Add logUtils.warn", "pr": 589 + }, + { + "note": "Fixes uncaught Error in abi_decoder", + "pr": 763 } ] }, diff --git a/packages/utils/package.json b/packages/utils/package.json index f4f39956ce..0ba7a7ba4b 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/utils", - "version": "0.7.3", + "version": "0.7.2", "engines": { "node": ">=6.12" }, @@ -35,15 +35,14 @@ "typescript": "2.7.1" }, "dependencies": { - "ethereum-types": "^0.0.1", + "ethereum-types": "^0.0.2", "@0xproject/typescript-typings": "^0.4.2", "@types/node": "^8.0.53", "ethereumjs-util": "^5.1.1", "bignumber.js": "~4.1.0", "ethers": "3.0.22", "js-sha3": "^0.7.0", - "lodash": "^4.17.4", - "web3": "^0.20.0" + "lodash": "^4.17.4" }, "publishConfig": { "access": "public" diff --git a/packages/web3-wrapper/CHANGELOG.json b/packages/web3-wrapper/CHANGELOG.json index 624ad71511..f8b1dab85e 100644 --- a/packages/web3-wrapper/CHANGELOG.json +++ b/packages/web3-wrapper/CHANGELOG.json @@ -1,4 +1,14 @@ [ + { + "timestamp": 1529397769, + "version": "0.7.2", + "changes": [ + { + "note": "Add `getNodeTypeAsync` method", + "pr": 812 + } + ] + }, { "timestamp": 1529397769, "version": "0.7.1", diff --git a/packages/web3-wrapper/package.json b/packages/web3-wrapper/package.json index f12142d3c0..2084cce73c 100644 --- a/packages/web3-wrapper/package.json +++ b/packages/web3-wrapper/package.json @@ -68,7 +68,7 @@ "@0xproject/typescript-typings": "^0.4.1", "@0xproject/utils": "^0.7.1", "bn.js": "^4.11.8", - "ethereum-types": "^0.0.1", + "ethereum-types": "^0.0.2", "ethereumjs-util": "^5.1.1", "ethers": "3.0.22", "lodash": "^4.17.4", diff --git a/packages/web3-wrapper/src/index.ts b/packages/web3-wrapper/src/index.ts index b14fa74065..66ef0a784e 100644 --- a/packages/web3-wrapper/src/index.ts +++ b/packages/web3-wrapper/src/index.ts @@ -1,2 +1,2 @@ -export { Web3Wrapper, uniqueVersionIds } from './web3_wrapper'; +export { Web3Wrapper, uniqueVersionIds, NodeType } from './web3_wrapper'; export { Web3WrapperErrors } from './types'; diff --git a/packages/web3-wrapper/src/web3_wrapper.ts b/packages/web3-wrapper/src/web3_wrapper.ts index 29adb18ff4..e56b99366c 100644 --- a/packages/web3-wrapper/src/web3_wrapper.ts +++ b/packages/web3-wrapper/src/web3_wrapper.ts @@ -39,6 +39,12 @@ export const uniqueVersionIds = { 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. */ @@ -595,6 +601,20 @@ export class Web3Wrapper { assert.isNumber('blockNumber', blockNumber); await this._sendRawPayloadAsync({ 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 { + 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(payload: Partial): Promise { const sendAsync = this._provider.sendAsync.bind(this._provider); const payloadWithDefaults = { diff --git a/packages/website/package.json b/packages/website/package.json index c7b689356c..a5768a60b3 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -38,6 +38,7 @@ "lodash": "^4.17.4", "material-ui": "^0.17.1", "moment": "2.21.0", + "numeral": "^2.0.6", "polished": "^1.9.2", "query-string": "^6.0.0", "react": "15.6.1", @@ -57,8 +58,7 @@ "styled-components": "^3.3.0", "thenby": "^1.2.3", "truffle-contract": "2.0.1", - "web3": "^0.20.0", - "web3-provider-engine": "^14.0.4", + "web3-provider-engine": "14.0.6", "whatwg-fetch": "^2.0.3", "xml-js": "^1.6.4" }, @@ -71,6 +71,7 @@ "@types/lodash": "4.14.104", "@types/material-ui": "0.18.0", "@types/node": "^8.0.53", + "@types/numeral": "^0.0.22", "@types/query-string": "^5.1.0", "@types/react": "16.3.13", "@types/react-copy-to-clipboard": "^4.2.0", diff --git a/packages/website/public/images/lock_icon.svg b/packages/website/public/images/lock_icon.svg new file mode 100644 index 0000000000..83e8191a10 --- /dev/null +++ b/packages/website/public/images/lock_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/website/public/images/setup_account_icon.svg b/packages/website/public/images/setup_account_icon.svg new file mode 100644 index 0000000000..eaa5b2fd68 --- /dev/null +++ b/packages/website/public/images/setup_account_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/website/public/images/team/alexbrowne.png b/packages/website/public/images/team/alexbrowne.png new file mode 100644 index 0000000000..76a61913e5 Binary files /dev/null and b/packages/website/public/images/team/alexbrowne.png differ diff --git a/packages/website/public/index.html b/packages/website/public/index.html index 4c0985c718..060f2c3c2d 100644 --- a/packages/website/public/index.html +++ b/packages/website/public/index.html @@ -70,7 +70,18 @@ })(document, 'script', 'twitter-wjs'); - + + + diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index d18c34c325..cc2afa28a9 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -12,10 +12,10 @@ import { import { isValidOrderHash, signOrderHashAsync } from '@0xproject/order-utils'; import { EtherscanLinkSuffixes, utils as sharedUtils } from '@0xproject/react-shared'; import { - InjectedWeb3Subprovider, ledgerEthereumBrowserClientFactoryAsync, LedgerSubprovider, RedundantSubprovider, + SignerSubprovider, Subprovider, } from '@0xproject/subproviders'; import { @@ -46,6 +46,7 @@ import { Fill, InjectedProviderObservable, InjectedProviderUpdate, + InjectedWeb3, Order as PortalOrder, Providers, ProviderType, @@ -59,7 +60,6 @@ import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; import { errorReporter } from 'ts/utils/error_reporter'; import { utils } from 'ts/utils/utils'; -import Web3 = require('web3'); import ProviderEngine = require('web3-provider-engine'); import FilterSubprovider = require('web3-provider-engine/subproviders/filters'); import RpcSubprovider = require('web3-provider-engine/subproviders/rpc'); @@ -73,6 +73,8 @@ const providerToName: { [provider: string]: string } = { [Providers.Metamask]: constants.PROVIDER_NAME_METAMASK, [Providers.Parity]: constants.PROVIDER_NAME_PARITY_SIGNER, [Providers.Mist]: constants.PROVIDER_NAME_MIST, + [Providers.Toshi]: constants.PROVIDER_NAME_TOSHI, + [Providers.Cipher]: constants.PROVIDER_NAME_CIPHER, }; export class Blockchain { @@ -95,8 +97,19 @@ export class Blockchain { } return providerNameIfExists; } - private static _getInjectedWeb3(): any { - return (window as any).web3; + private static _getInjectedWeb3(): InjectedWeb3 { + 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 { // 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; } private static async _getProviderAsync( - injectedWeb3: Web3, + injectedWeb3: InjectedWeb3, networkIdIfExists: number, shouldUserLedgerProvider: boolean = false, ): 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 // instance. All other requests go to the public hosted node. const provider = new ProviderEngine(); - provider.addProvider(new InjectedWeb3Subprovider(injectedWeb3.currentProvider)); + provider.addProvider(new SignerSubprovider(injectedWeb3.currentProvider)); provider.addProvider(new FilterSubprovider()); const rpcSubproviders = _.map(publicNodeUrlsIfExistsForNetworkId, publicNodeUrl => { return new RpcSubprovider({ @@ -832,10 +845,10 @@ export class Blockchain { this._dispatcher.updateNetworkId(networkId); await this._rehydrateStoreWithContractEventsAsync(); } - private _updateProviderName(injectedWeb3: Web3): void { - const doesInjectedWeb3Exist = !_.isUndefined(injectedWeb3); + private _updateProviderName(injectedWeb3IfExists: InjectedWeb3): void { + const doesInjectedWeb3Exist = !_.isUndefined(injectedWeb3IfExists); const providerName = doesInjectedWeb3Exist - ? Blockchain._getNameGivenProvider(injectedWeb3.currentProvider) + ? Blockchain._getNameGivenProvider(injectedWeb3IfExists.currentProvider) : constants.PROVIDER_NAME_PUBLIC; this._dispatcher.updateInjectedProviderName(providerName); } diff --git a/packages/website/ts/blockchain_watcher.ts b/packages/website/ts/blockchain_watcher.ts index df5f73fd19..4b23aa98af 100644 --- a/packages/website/ts/blockchain_watcher.ts +++ b/packages/website/ts/blockchain_watcher.ts @@ -10,6 +10,7 @@ export class BlockchainWatcher { private _watchBalanceIntervalId: NodeJS.Timer; private _prevUserEtherBalanceInWei?: BigNumber; private _prevUserAddressIfExists: string; + private _prevNodeVersionIfExists: string; constructor(dispatcher: Dispatcher, web3Wrapper: Web3Wrapper, shouldPollUserAddress: boolean) { this._dispatcher = dispatcher; this._shouldPollUserAddress = shouldPollUserAddress; @@ -43,11 +44,9 @@ export class BlockchainWatcher { ); } private async _updateBalanceAsync(): Promise { - let prevNodeVersion: string; - // Check for node version changes const currentNodeVersion = await this._web3Wrapper.getNodeVersionAsync(); - if (currentNodeVersion !== prevNodeVersion) { - prevNodeVersion = currentNodeVersion; + if (this._prevNodeVersionIfExists !== currentNodeVersion) { + this._prevNodeVersionIfExists = currentNodeVersion; this._dispatcher.updateNodeVersion(currentNodeVersion); } diff --git a/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx b/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx index 9ac78e80e7..7b09cc92c5 100644 --- a/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx +++ b/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx @@ -103,7 +103,6 @@ export class EthWethConversionDialog extends React.Component< shouldCheckAllowance={false} onChange={this._onValueChange.bind(this)} amount={this.state.value} - onVisitBalancesPageClick={this.props.onCancelled} /> ) : ( )}
diff --git a/packages/website/ts/components/dialogs/send_dialog.tsx b/packages/website/ts/components/dialogs/send_dialog.tsx index 421f18b4fe..8a98fdf69a 100644 --- a/packages/website/ts/components/dialogs/send_dialog.tsx +++ b/packages/website/ts/components/dialogs/send_dialog.tsx @@ -80,7 +80,6 @@ export class SendDialog extends React.Component
diff --git a/packages/website/ts/components/inputs/balance_bounded_input.tsx b/packages/website/ts/components/inputs/balance_bounded_input.tsx index 968609030e..f23beb436d 100644 --- a/packages/website/ts/components/inputs/balance_bounded_input.tsx +++ b/packages/website/ts/components/inputs/balance_bounded_input.tsx @@ -3,9 +3,8 @@ import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; import TextField from 'material-ui/TextField'; import * as React from 'react'; -import { Link } from 'react-router-dom'; 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'; interface BalanceBoundedInputProps { @@ -18,8 +17,6 @@ interface BalanceBoundedInputProps { shouldShowIncompleteErrs?: boolean; shouldCheckBalance: boolean; validate?: (amount: BigNumber) => React.ReactNode; - onVisitBalancesPageClick?: () => void; - shouldHideVisitBalancesLink?: boolean; isDisabled?: boolean; shouldShowErrs?: boolean; shouldShowUnderline?: boolean; @@ -35,7 +32,6 @@ interface BalanceBoundedInputState { export class BalanceBoundedInput extends React.Component { public static defaultProps: Partial = { shouldShowIncompleteErrs: false, - shouldHideVisitBalancesLink: false, isDisabled: false, shouldShowErrs: true, hintText: 'amount', @@ -124,38 +120,11 @@ export class BalanceBoundedInput extends React.ComponentInsufficient balance. {this._renderIncreaseBalanceLink()}; + return Insufficient balance.; } const errMsg = _.isUndefined(this.props.validate) ? undefined : this.props.validate(amount); 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 ( - - {increaseBalanceText} - - ); - } else { - return ( -
- {increaseBalanceText} -
- ); - } - } - private _setAmountState(amount: string, balance: BigNumber, callback: () => void = _.noop): void { const errorMsg = this._validate(amount, balance); this.props.onErrorMsgChange(errorMsg); diff --git a/packages/website/ts/components/inputs/eth_amount_input.tsx b/packages/website/ts/components/inputs/eth_amount_input.tsx index 1f0f274105..552d4277a2 100644 --- a/packages/website/ts/components/inputs/eth_amount_input.tsx +++ b/packages/website/ts/components/inputs/eth_amount_input.tsx @@ -14,9 +14,7 @@ interface EthAmountInputProps { onChange: ValidatedBigNumberCallback; onErrorMsgChange?: (errorMsg: React.ReactNode) => void; shouldShowIncompleteErrs: boolean; - onVisitBalancesPageClick?: () => void; shouldCheckBalance: boolean; - shouldHideVisitBalancesLink?: boolean; shouldShowErrs?: boolean; shouldShowUnderline?: boolean; style?: React.CSSProperties; @@ -46,8 +44,6 @@ export class EthAmountInput extends React.Component void; - onVisitBalancesPageClick?: () => void; lastForceTokenStateRefetch: number; shouldShowErrs?: boolean; shouldShowUnderline?: boolean; @@ -88,7 +87,6 @@ export class TokenAmountInput extends React.Component = ({ borderRadius, }) => ( - +
{title} diff --git a/packages/website/ts/components/onboarding/onboarding_flow.tsx b/packages/website/ts/components/onboarding/onboarding_flow.tsx index 1f4c6df82d..c2b4a4ca71 100644 --- a/packages/website/ts/components/onboarding/onboarding_flow.tsx +++ b/packages/website/ts/components/onboarding/onboarding_flow.tsx @@ -6,13 +6,29 @@ import { ContinueButtonDisplay, OnboardingTooltip } from 'ts/components/onboardi import { Animation } from 'ts/components/ui/animation'; import { Container } from 'ts/components/ui/container'; import { Overlay } from 'ts/components/ui/overlay'; +import { PointerDirection } from 'ts/components/ui/pointer'; 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; + placement: Placement; +} + +export interface Step { + // Provide either a CSS selector, or fixed position settings. Only applies to desktop. + position: TargetPositionSettings | FixedPositionSettings; title?: string; content: React.ReactNode; - placement?: Placement; shouldHideBackButton?: boolean; shouldHideNextButton?: boolean; continueButtonDisplay?: ContinueButtonDisplay; @@ -40,18 +56,30 @@ export class OnboardingFlow extends React.Component { return null; } let onboardingElement = null; + const currentStep = this._getCurrentStep(); if (this.props.isMobile) { - onboardingElement = {this._renderOnboardignCard()}; - } else { + onboardingElement = {this._renderOnboardingCard()}; + } else if (currentStep.position.type === 'target') { + const { placement, target } = currentStep.position; onboardingElement = ( - + {this._renderPopperChildren.bind(this)} ); + } else if (currentStep.position.type === 'fixed') { + const { top, right, bottom, left, pointerDirection } = currentStep.position; + onboardingElement = ( + + {this._renderToolTip(pointerDirection)} + + ); } if (this.props.disableOverlay) { return onboardingElement; @@ -63,9 +91,6 @@ export class OnboardingFlow extends React.Component {
); } - private _getElementForStep(): Element { - return document.querySelector(this._getCurrentStep().target); - } private _renderPopperChildren(props: PopperChildrenProps): React.ReactNode { const customStyles = { zIndex: zIndex.aboveOverlay }; // On re-render, we want to re-center the popper. @@ -76,7 +101,7 @@ export class OnboardingFlow extends React.Component {
); } - private _renderToolTip(): React.ReactNode { + private _renderToolTip(pointerDirection?: PointerDirection): React.ReactNode { const { steps, stepIndex } = this.props; const step = steps[stepIndex]; const isLastStep = steps.length - 1 === stepIndex; @@ -94,12 +119,13 @@ export class OnboardingFlow extends React.Component { continueButtonDisplay={step.continueButtonDisplay} continueButtonText={step.continueButtonText} onContinueButtonClick={step.onContinueButtonClick} + pointerDirection={pointerDirection} />
); } - private _renderOnboardignCard(): React.ReactNode { + private _renderOnboardingCard(): React.ReactNode { const { steps, stepIndex } = this.props; const step = steps[stepIndex]; const isLastStep = steps.length - 1 === stepIndex; diff --git a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx index 6bfa5c75ff..b7c5a9f64c 100644 --- a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx +++ b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx @@ -9,7 +9,12 @@ import { AddEthOnboardingStep } from 'ts/components/onboarding/add_eth_onboardin import { CongratsOnboardingStep } from 'ts/components/onboarding/congrats_onboarding_step'; import { InstallWalletOnboardingStep } from 'ts/components/onboarding/install_wallet_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 { UnlockWalletOnboardingStep } from 'ts/components/onboarding/unlock_wallet_onboarding_step'; import { @@ -45,8 +50,6 @@ class PlainPortalOnboardingFlow extends React.Component void; public componentDidMount(): void { 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. this._unlisten = this.props.history.listen(() => this.props.updateIsRunning(false)); } @@ -61,6 +64,9 @@ class PlainPortalOnboardingFlow extends React.Component, - placement: 'right', shouldHideBackButton: true, shouldHideNextButton: true, }, { - target: '.wallet', + position: underMetamaskExtension, title: '0x Ecosystem Setup', content: , - placement: 'right', shouldHideBackButton: true, shouldHideNextButton: true, }, { - target: '.wallet', + position: nextToWalletPosition, title: '0x Ecosystem Account Setup', content: , - placement: 'right', shouldHideBackButton: true, continueButtonDisplay: 'enabled', }, { - target: '.wallet', + position: nextToWalletPosition, title: 'Step 1: Add ETH', content: ( ), - placement: 'right', continueButtonDisplay: this._userHasVisibleEth() ? 'enabled' : 'disabled', }, { - target: '.wallet', + position: nextToWalletPosition, title: 'Step 2: Wrap ETH', content: , - placement: 'right', continueButtonDisplay: 'enabled', }, { - target: '.wallet', + position: nextToWalletPosition, title: 'Step 2: Wrap ETH', content: , - placement: 'right', continueButtonDisplay: this._userHasVisibleWeth() ? 'enabled' : 'disabled', }, { - target: '.wallet', + position: nextToWalletPosition, title: 'Step 2: Wrap ETH', content: ( ), - placement: 'right', continueButtonDisplay: this._userHasVisibleWeth() ? 'enabled' : 'disabled', }, { - target: '.wallet', + position: nextToWalletPosition, title: 'Step 3: Unlock Tokens', content: ( ), - placement: 'right', continueButtonDisplay: this._doesUserHaveAllowancesForWethAndZrx() ? 'enabled' : 'disabled', }, { - target: '.wallet', + position: nextToWalletPosition, title: '🎉 The Ecosystem Awaits', content: , - placement: 'right', continueButtonDisplay: 'enabled', shouldHideNextButton: true, continueButtonText: 'Enter the 0x Ecosystem', @@ -221,7 +229,7 @@ class PlainPortalOnboardingFlow extends React.Component
- Unlock your metamask extension to get started. + Unlock your MetaMask extension to get started. ); diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx index 9c0cb866d6..8c3b5cfd71 100644 --- a/packages/website/ts/components/portal/portal.tsx +++ b/packages/website/ts/components/portal/portal.tsx @@ -1,7 +1,6 @@ import { colors, constants as sharedConstants } from '@0xproject/react-shared'; import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; -import Help from 'material-ui/svg-icons/action/help'; import * as React from 'react'; import * as DocumentTitle from 'react-document-title'; 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 { Container } from 'ts/components/ui/container'; import { FlashMessage } from 'ts/components/ui/flash_message'; +import { Image } from 'ts/components/ui/image'; import { Text } from 'ts/components/ui/text'; import { Wallet } from 'ts/components/wallet/wallet'; import { GenerateOrderForm } from 'ts/containers/generate_order_form'; @@ -368,11 +368,11 @@ export class Portal extends React.Component { const shouldStartOnboarding = !isMobile || this.props.location.pathname === `${WebsitePaths.Portal}/account`; const startOnboarding = ( - - - - Learn how to set up your account - + + Set up your account to start trading + + + ); diff --git a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx index b26bf512bc..02bc1b0146 100644 --- a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx +++ b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx @@ -9,6 +9,7 @@ import { Container } from 'ts/components/ui/container'; import { Image } from 'ts/components/ui/image'; import { Island } from 'ts/components/ui/island'; import { colors } from 'ts/style/colors'; +import { media } from 'ts/style/media'; import { styled } from 'ts/style/theme'; import { WebsiteBackendRelayerInfo } from 'ts/types'; import { utils } from 'ts/utils/utils'; @@ -55,7 +56,7 @@ const styles: Styles = { }; 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 RELAYER_ICON_HEIGHT = '110px'; @@ -111,6 +112,9 @@ const GridTile = styled(PlainGridTile)` &:hover { transform: translate(0px, -3px); } + ${media.small` + transform: none; + `}; `; interface SectionProps { diff --git a/packages/website/ts/components/top_bar/provider_display.tsx b/packages/website/ts/components/top_bar/provider_display.tsx index 8743e43207..806eaeea50 100644 --- a/packages/website/ts/components/top_bar/provider_display.tsx +++ b/packages/website/ts/components/top_bar/provider_display.tsx @@ -1,23 +1,26 @@ import { Styles } from '@0xproject/react-shared'; import * as _ from 'lodash'; import CircularProgress from 'material-ui/CircularProgress'; -import RaisedButton from 'material-ui/RaisedButton'; 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 { Blockchain } from 'ts/blockchain'; -import { ProviderPicker } from 'ts/components/top_bar/provider_picker'; import { AccountConnection } from 'ts/components/ui/account_connection'; import { Container } from 'ts/components/ui/container'; import { DropDown } from 'ts/components/ui/drop_down'; import { Identicon } from 'ts/components/ui/identicon'; +import { Image } from 'ts/components/ui/image'; 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 { Dispatcher } from 'ts/redux/dispatcher'; import { colors } from 'ts/style/colors'; import { AccountState, ProviderType } from 'ts/types'; -import { constants } from 'ts/utils/constants'; import { utils } from 'ts/utils/utils'; const ROOT_HEIGHT = 24; @@ -44,11 +47,7 @@ const styles: Styles = { export class ProviderDisplay extends React.Component { public render(): React.ReactNode { - const isExternallyInjectedProvider = utils.isExternallyInjected( - this.props.providerType, - this.props.injectedProviderName, - ); - const hoverActiveNode = ( + const activeNode = ( {this._renderIcon()} @@ -57,93 +56,34 @@ export class ProviderDisplay extends React.Component ); - const hasLedgerProvider = this.props.providerType === ProviderType.Ledger; - const horizontalPosition = isExternallyInjectedProvider || hasLedgerProvider ? 'left' : 'middle'; return (
); } - public renderPopoverContent(hasInjectedProvider: boolean, hasLedgerProvider: boolean): React.ReactNode { - if (!this._isBlockchainReady()) { - return null; - } else if (hasInjectedProvider || hasLedgerProvider) { - return ( - - ); - } else { - // Nothing to connect to, show install/info popover - return ( -
-
- Choose a wallet: -
-
- -
-
-
or
-
-
-
-
Connect to a ledger hardware wallet
-
- -
-
- -
-
-
-
- ); + private _renderPopoverContent(): React.ReactNode { + const accountState = this._getAccountState(); + switch (accountState) { + case AccountState.Ready: + return ( + + + + + + ); + case AccountState.Disconnected: + case AccountState.Locked: + case AccountState.Loading: + default: + return null; } } private _renderIcon(): React.ReactNode { @@ -154,7 +94,7 @@ export class ProviderDisplay extends React.Component; case AccountState.Locked: - return ; + return ; case AccountState.Disconnected: return ; default: diff --git a/packages/website/ts/components/top_bar/provider_picker.tsx b/packages/website/ts/components/top_bar/provider_picker.tsx deleted file mode 100644 index 7937f2e9db..0000000000 --- a/packages/website/ts/components/top_bar/provider_picker.tsx +++ /dev/null @@ -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 { - 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 ( -
- - - - -
- ); - } - private _renderLabel(title: string, shouldShowNetwork: boolean): React.ReactNode { - const label = ( -
-
{title}
- {shouldShowNetwork && this._renderNetwork()} -
- ); - return label; - } - private _renderNetwork(): React.ReactNode { - const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId]; - return ( -
-
- -
-
{networkName}
-
- ); - } - private _onProviderRadioChanged(value: string): void { - if (value === ProviderType.Ledger) { - this.props.onToggleLedgerDialog(); - } else { - // tslint:disable-next-line:no-floating-promises - this.props.blockchain.updateProviderToInjectedAsync(); - } - } -} diff --git a/packages/website/ts/components/top_bar/top_bar.tsx b/packages/website/ts/components/top_bar/top_bar.tsx index fac6c131f3..7785366637 100644 --- a/packages/website/ts/components/top_bar/top_bar.tsx +++ b/packages/website/ts/components/top_bar/top_bar.tsx @@ -199,7 +199,7 @@ export class TopBar extends React.Component { cursor: 'pointer', paddingTop: 16, }; - const hoverActiveNode = ( + const activeNode = (
{this.props.translate.get(Key.Developers, Deco.Cap)}
@@ -224,7 +224,7 @@ export class TopBar extends React.Component {
{ public static defaultProps: Partial = { style: DEFAULT_STYLE, zDepth: 1, + activateEvent: DropdownMouseEvent.Hover, + closeEvent: DropdownMouseEvent.Hover, }; private _isHovering: boolean; private _popoverCloseCheckIntervalId: number; @@ -58,46 +67,61 @@ export class DropDown extends React.Component { onMouseEnter={this._onHover.bind(this)} onMouseLeave={this._onHoverOff.bind(this)} > - {this.props.hoverActiveNode} +
{this.props.activeNode}
-
+
{this.props.popoverContent}
); } + private _onActiveNodeClick(event: React.FormEvent): void { + if (this.props.activateEvent === DropdownMouseEvent.Click) { + this.setState({ + isDropDownOpen: true, + anchorEl: event.currentTarget, + }); + } + } private _onHover(event: React.FormEvent): void { 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): void { if (this.state.isDropDownOpen) { return; // noop } - this.setState({ isDropDownOpen: true, anchorEl: event.currentTarget, }); } - private _onHoverOff(): void { - this._isHovering = false; - } private _checkIfShouldClosePopover(): void { - if (!this.state.isDropDownOpen || this._isHovering) { + if (!this.state.isDropDownOpen) { return; // noop } - this._closePopover(); + if (this.props.closeEvent === DropdownMouseEvent.Hover && !this._isHovering) { + this._closePopover(); + } } private _closePopover(): void { this.setState({ diff --git a/packages/website/ts/components/ui/simple_menu.tsx b/packages/website/ts/components/ui/simple_menu.tsx new file mode 100644 index 0000000000..74b8ef6aef --- /dev/null +++ b/packages/website/ts/components/ui/simple_menu.tsx @@ -0,0 +1,88 @@ +import * as _ from 'lodash'; +import * as React from 'react'; +import * as CopyToClipboard from 'react-copy-to-clipboard'; +import { Link } from 'react-router-dom'; + +import { Container } from 'ts/components/ui/container'; +import { Text } from 'ts/components/ui/text'; +import { colors } from 'ts/style/colors'; +import { WebsitePaths } from 'ts/types'; + +export interface SimpleMenuProps { + minWidth?: number | string; +} + +export const SimpleMenu: React.StatelessComponent = ({ children, minWidth }) => { + return ( + + {children} + + ); +}; + +SimpleMenu.defaultProps = { + minWidth: '220px', +}; + +export interface SimpleMenuItemProps { + displayText: string; + onClick?: () => void; +} +export const SimpleMenuItem: React.StatelessComponent = ({ displayText, onClick }) => { + // Falling back to _.noop for onclick retains the hovering effect + return ( + + + {displayText} + + + ); +}; + +export interface CopyAddressSimpleMenuItemProps { + userAddress: string; + onClick?: () => void; +} +export const CopyAddressSimpleMenuItem: React.StatelessComponent = ({ + userAddress, + onClick, +}) => { + return ( + + + + ); +}; + +export interface GoToAccountManagementSimpleMenuItemProps { + onClick?: () => void; +} +export const GoToAccountManagementSimpleMenuItem: React.StatelessComponent< + GoToAccountManagementSimpleMenuItemProps +> = ({ onClick }) => { + return ( + + + + ); +}; + +export interface DifferentWalletSimpleMenuItemProps { + onClick?: () => void; +} +export const DifferentWalletSimpleMenuItem: React.StatelessComponent = ({ + onClick, +}) => { + return ; +}; diff --git a/packages/website/ts/components/ui/text.tsx b/packages/website/ts/components/ui/text.tsx index c1cb2ade43..315f728549 100644 --- a/packages/website/ts/components/ui/text.tsx +++ b/packages/website/ts/components/ui/text.tsx @@ -3,7 +3,7 @@ import { darken } from 'polished'; import * as React from 'react'; import { styled } from 'ts/style/theme'; -export type TextTag = 'p' | 'div' | 'span' | 'label' | 'h1' | 'h2' | 'h3' | 'h4'; +export type TextTag = 'p' | 'div' | 'span' | 'label' | 'h1' | 'h2' | 'h3' | 'h4' | 'i'; export interface TextProps { className?: string; @@ -17,6 +17,7 @@ export interface TextProps { fontWeight?: number | string; textDecorationLine?: string; onClick?: (event: React.MouseEvent) => void; + hoverColor?: string; } const PlainText: React.StatelessComponent = ({ children, className, onClick, Tag }) => ( @@ -37,7 +38,7 @@ export const Text = styled(PlainText)` ${props => (props.onClick ? 'cursor: pointer' : '')}; transition: color 0.5s ease; &:hover { - ${props => (props.onClick ? `color: ${darken(0.3, props.fontColor)}` : '')}; + ${props => (props.onClick ? `color: ${props.hoverColor || darken(0.3, props.fontColor)}` : '')}; } `; diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx index 1f1e3598aa..1e0b9ec485 100644 --- a/packages/website/ts/components/wallet/wallet.tsx +++ b/packages/website/ts/components/wallet/wallet.tsx @@ -1,19 +1,25 @@ import { constants as sharedConstants, EtherscanLinkSuffixes, utils as sharedUtils } from '@0xproject/react-shared'; import { BigNumber, errorUtils } from '@0xproject/utils'; -import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as _ from 'lodash'; import ActionAccountBalanceWallet from 'material-ui/svg-icons/action/account-balance-wallet'; import * as React from 'react'; -import { Link } from 'react-router-dom'; import firstBy = require('thenby'); import { Blockchain } from 'ts/blockchain'; import { AccountConnection } from 'ts/components/ui/account_connection'; import { Container } from 'ts/components/ui/container'; +import { DropDown, DropdownMouseEvent } from 'ts/components/ui/drop_down'; import { IconButton } from 'ts/components/ui/icon_button'; import { Identicon } from 'ts/components/ui/identicon'; import { Island } from 'ts/components/ui/island'; +import { + CopyAddressSimpleMenuItem, + DifferentWalletSimpleMenuItem, + GoToAccountManagementSimpleMenuItem, + SimpleMenu, + SimpleMenuItem, +} from 'ts/components/ui/simple_menu'; import { Text } from 'ts/components/ui/text'; import { TokenIcon } from 'ts/components/ui/token_icon'; import { BodyOverlay } from 'ts/components/wallet/body_overlay'; @@ -34,7 +40,6 @@ import { TokenByAddress, TokenState, TokenStateByAddress, - WebsitePaths, } from 'ts/types'; import { analytics } from 'ts/utils/analytics'; import { constants } from 'ts/utils/constants'; @@ -83,9 +88,7 @@ const ICON_DIMENSION = 28; const BODY_ITEM_KEY = 'BODY'; const HEADER_ITEM_KEY = 'HEADER'; const ETHER_ITEM_KEY = 'ETHER'; -const USD_DECIMAL_PLACES = 2; const NO_ALLOWANCE_TOGGLE_SPACE_WIDTH = 56; -const ACCOUNT_PATH = `${WebsitePaths.Portal}/account`; const PLACEHOLDER_COLOR = colors.grey300; const LOADING_ROWS_COUNT = 6; @@ -189,6 +192,7 @@ export class Wallet extends React.Component { ); } private _renderConnectedHeaderRows(): React.ReactElement<{}> { + const isMobile = this.props.screenWidth === ScreenWidths.Sm; const userAddress = this.props.userAddress; const accountState = this._getAccountState(); const main = ( @@ -199,15 +203,49 @@ export class Wallet extends React.Component {
); + const onClick = _.noop; + const accessory = ( + + + + } + popoverContent={ + + + + + + + + } + anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }} + targetOrigin={{ horizontal: 'right', vertical: 'top' }} + zDepth={1} + activateEvent={DropdownMouseEvent.Click} + closeEvent={isMobile ? DropdownMouseEvent.Click : DropdownMouseEvent.Hover} + /> + ); return ( - - } - main={main} - minHeight="60px" - backgroundColor={colors.white} - /> - + } + main={main} + accessory={accessory} + minHeight="60px" + backgroundColor={colors.white} + /> ); } private _renderBody(): React.ReactElement<{}> { @@ -320,8 +358,24 @@ export class Wallet extends React.Component { this.state.wrappedEtherDirection === accessoryItemConfig.wrappedEtherDirection && !_.isUndefined(this.props.userEtherBalanceInWei); const etherToken = this._getEthToken(); + const wrapEtherItem = shouldShowWrapEtherItem ? ( + this.props.refetchTokenStateAsync(etherToken.address)} + /> + ) : null; return (
+ {this.state.wrappedEtherDirection === Side.Receive && wrapEtherItem} {
} accessory={this._renderAccessoryItems(accessoryItemConfig)} - backgroundColor={shouldShowWrapEtherItem ? colors.walletFocusedItemBackground : undefined} /> - {shouldShowWrapEtherItem && ( - this.props.refetchTokenStateAsync(etherToken.address)} - /> - )} + {this.state.wrappedEtherDirection === Side.Deposit && wrapEtherItem}
); } @@ -411,19 +450,11 @@ export class Wallet extends React.Component { price?: BigNumber, isLoading: boolean = false, ): React.ReactNode { - let result; - if (!isLoading) { - if (_.isUndefined(price)) { - result = '--'; - } else { - const unitAmount = Web3Wrapper.toUnitAmount(amount, decimals); - const value = unitAmount.mul(price); - const formattedAmount = value.toFixed(USD_DECIMAL_PLACES); - result = `$${formattedAmount}`; - } - } else { - result = '$0.00'; - } + const result = !isLoading + ? _.isUndefined(price) + ? '--' + : utils.getUsdValueFormattedAmount(amount, decimals, price) + : '$0.00'; return ( diff --git a/packages/website/ts/components/wallet/wrap_ether_item.tsx b/packages/website/ts/components/wallet/wrap_ether_item.tsx index 851b35f90a..2b4cf93fe8 100644 --- a/packages/website/ts/components/wallet/wrap_ether_item.tsx +++ b/packages/website/ts/components/wallet/wrap_ether_item.tsx @@ -8,6 +8,7 @@ import * as React from 'react'; import { Blockchain } from 'ts/blockchain'; import { EthAmountInput } from 'ts/components/inputs/eth_amount_input'; import { TokenAmountInput } from 'ts/components/inputs/token_amount_input'; +import { Container } from 'ts/components/ui/container'; import { Dispatcher } from 'ts/redux/dispatcher'; import { colors } from 'ts/style/colors'; import { BlockchainCallErrs, Side, Token } from 'ts/types'; @@ -94,7 +95,7 @@ export class WrapEtherItem extends React.Component +
{this._renderIsEthConversionHappeningSpinner()}
{topLabelText}
@@ -142,7 +143,7 @@ export class WrapEtherItem extends React.Component -
+
); } private _onValueChange(_isValid: boolean, amount?: BigNumber): void { diff --git a/packages/website/ts/containers/subproviders_documentation.ts b/packages/website/ts/containers/subproviders_documentation.ts index 6d4230e535..567f6a37e4 100644 --- a/packages/website/ts/containers/subproviders_documentation.ts +++ b/packages/website/ts/containers/subproviders_documentation.ts @@ -25,6 +25,7 @@ const docSections = { emptyWalletSubprovider: 'emptyWalletSubprovider', fakeGasEstimateSubprovider: 'fakeGasEstimateSubprovider', injectedWeb3Subprovider: 'injectedWeb3Subprovider', + signerSubprovider: 'signerSubprovider', redundantRPCSubprovider: 'redundantRPCSubprovider', ganacheSubprovider: 'ganacheSubprovider', nonceTrackerSubprovider: 'nonceTrackerSubprovider', @@ -50,6 +51,7 @@ const docsInfoConfig: DocsInfoConfig = { ['emptyWallet-subprovider']: [docSections.emptyWalletSubprovider], ['fakeGasEstimate-subprovider']: [docSections.fakeGasEstimateSubprovider], ['injectedWeb3-subprovider']: [docSections.injectedWeb3Subprovider], + ['signer-subprovider']: [docSections.signerSubprovider], ['redundantRPC-subprovider']: [docSections.redundantRPCSubprovider], ['ganache-subprovider']: [docSections.ganacheSubprovider], ['nonceTracker-subprovider']: [docSections.nonceTrackerSubprovider], @@ -69,6 +71,7 @@ const docsInfoConfig: DocsInfoConfig = { [docSections.emptyWalletSubprovider]: ['"subproviders/src/subproviders/empty_wallet_subprovider"'], [docSections.fakeGasEstimateSubprovider]: ['"subproviders/src/subproviders/fake_gas_estimate_subprovider"'], [docSections.injectedWeb3Subprovider]: ['"subproviders/src/subproviders/injected_web3"'], + [docSections.signerSubprovider]: ['"subproviders/src/subproviders/signer"'], [docSections.redundantRPCSubprovider]: ['"subproviders/src/subproviders/redundant_rpc"'], [docSections.ganacheSubprovider]: ['"subproviders/src/subproviders/ganache"'], [docSections.nonceTrackerSubprovider]: ['"subproviders/src/subproviders/nonce_tracker"'], diff --git a/packages/website/ts/pages/about/about.tsx b/packages/website/ts/pages/about/about.tsx index 0259af36f3..5bb5d06a9e 100644 --- a/packages/website/ts/pages/about/about.tsx +++ b/packages/website/ts/pages/about/about.tsx @@ -165,16 +165,24 @@ const teamRow5: ProfileInfo[] = [ }, ]; -// const teamRow6: ProfileInfo[] = [ -// { -// name: 'Chris Kalani', -// title: 'Director of Design', -// description: `Previously founded Wake (acquired by InVision). Early Facebook product designer.`, -// image: 'images/team/chris.png', -// linkedIn: 'https://www.linkedin.com/in/chriskalani/', -// github: 'https://github.com/chriskalani', -// }, -// ]; +const teamRow6: ProfileInfo[] = [ + { + name: 'Alex Browne', + title: 'Engineer in Residence', + description: `Full-stack blockchain engineer. Previously at Plaid. Open source guru and footgun dismantler. Computer Science and Electrical Engineering at Duke.`, + image: 'images/team/alexbrowne.png', + linkedIn: 'https://www.linkedin.com/in/stephenalexbrowne/', + github: 'http://github.com/albrow', + }, + // { + // name: 'Chris Kalani', + // title: 'Director of Design', + // description: `Previously founded Wake (acquired by InVision). Early Facebook product designer.`, + // image: 'images/team/chris.png', + // linkedIn: 'https://www.linkedin.com/in/chriskalani/', + // github: 'https://github.com/chriskalani', + // }, +]; const advisors: ProfileInfo[] = [ { @@ -270,6 +278,7 @@ export class About extends React.Component {
{this._renderProfiles(teamRow3)}
{this._renderProfiles(teamRow4)}
{this._renderProfiles(teamRow5)}
+
{this._renderProfiles(teamRow6)}
= createStore( ); store.subscribe( _.throttle(() => { + const state = store.getState(); // Persisted state stateStorage.saveState({ - hasPortalOnboardingBeenClosed: store.getState().hasPortalOnboardingBeenClosed, + hasPortalOnboardingBeenClosed: state.hasPortalOnboardingBeenClosed, + isPortalOnboardingShowing: state.isPortalOnboardingShowing, }); }, ONE_SECOND), ); diff --git a/packages/website/ts/style/media.ts b/packages/website/ts/style/media.ts new file mode 100644 index 0000000000..3c992eb9f7 --- /dev/null +++ b/packages/website/ts/style/media.ts @@ -0,0 +1,14 @@ +import { css } from 'ts/style/theme'; +import { ScreenWidths } from 'ts/types'; + +const generateMediaWrapper = (screenWidth: ScreenWidths) => (...args: any[]) => css` + @media (max-width: ${screenWidth}) { + ${css.apply(css, args)}; + } +`; + +export const media = { + small: generateMediaWrapper(ScreenWidths.Sm), + medium: generateMediaWrapper(ScreenWidths.Md), + large: generateMediaWrapper(ScreenWidths.Lg), +}; diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index 498a0a5b81..d9b2ef618a 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -1,5 +1,6 @@ import { ECSignature } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; +import { Provider } from 'ethereum-types'; import * as React from 'react'; export enum Side { @@ -215,10 +216,11 @@ export interface ContractEvent { } export type ValidatedBigNumberCallback = (isValid: boolean, amount?: BigNumber) => void; +// Associated values are in `em` units export enum ScreenWidths { - Sm = 'SM', - Md = 'MD', - Lg = 'LG', + Sm = 40, + Md = 52, + Lg = 64, } export enum AlertTypes { @@ -487,6 +489,8 @@ export enum Providers { Parity = 'PARITY', Metamask = 'METAMASK', Mist = 'MIST', + Toshi = 'TOSHI', + Cipher = 'CIPHER', } export interface InjectedProviderUpdate { @@ -571,4 +575,16 @@ export enum AccountState { Loading = 'Loading', Locked = 'Locked', } + +export interface InjectedProvider extends Provider { + publicConfigStore?: InjectedProviderObservable; +} + +// Minimal expected interface for an injected web3 object +export interface InjectedWeb3 { + currentProvider: InjectedProvider; + version: { + getNetwork(cd: (err: Error, networkId: string) => void): void; + }; +} // tslint:disable:max-file-line-count diff --git a/packages/website/ts/utils/analytics.ts b/packages/website/ts/utils/analytics.ts index 928e45bc33..f4bfa083ff 100644 --- a/packages/website/ts/utils/analytics.ts +++ b/packages/website/ts/utils/analytics.ts @@ -1,8 +1,8 @@ import * as _ from 'lodash'; import * as ReactGA from 'react-ga'; +import { InjectedWeb3 } from 'ts/types'; import { configs } from 'ts/utils/configs'; import { utils } from 'ts/utils/utils'; -import * as Web3 from 'web3'; export const analytics = { init(): void { @@ -16,11 +16,12 @@ export const analytics = { value, }); }, - async logProviderAsync(web3IfExists: Web3): Promise { + async logProviderAsync(web3IfExists: InjectedWeb3): Promise { await utils.onPageLoadAsync(); - const providerType = !_.isUndefined(web3IfExists) - ? utils.getProviderType(web3IfExists.currentProvider) - : 'NONE'; + const providerType = + !_.isUndefined(web3IfExists) && !_.isUndefined(web3IfExists.currentProvider) + ? utils.getProviderType(web3IfExists.currentProvider) + : 'NONE'; ReactGA.ga('set', 'dimension1', providerType); }, }; diff --git a/packages/website/ts/utils/constants.ts b/packages/website/ts/utils/constants.ts index d3a2aa1078..0c4b887805 100644 --- a/packages/website/ts/utils/constants.ts +++ b/packages/website/ts/utils/constants.ts @@ -6,7 +6,7 @@ export const constants = { ETHER_TOKEN_SYMBOL: 'WETH', ZRX_TOKEN_SYMBOL: 'ZRX', ETHER_SYMBOL: 'ETH', - TOKEN_AMOUNT_DISPLAY_PRECISION: 5, + TOKEN_AMOUNT_DISPLAY_PRECISION: 4, GENESIS_ORDER_BLOCK_BY_NETWORK_ID: { 1: 4145578, 42: 3117574, @@ -29,6 +29,8 @@ export const constants = { PROVIDER_NAME_METAMASK: 'MetaMask', PROVIDER_NAME_PARITY_SIGNER: 'Parity Signer', PROVIDER_NAME_MIST: 'Mist', + PROVIDER_NAME_CIPHER: 'Cipher Browser', + PROVIDER_NAME_TOSHI: 'Toshi', PROVIDER_NAME_GENERIC: 'Injected Web3', PROVIDER_NAME_PUBLIC: '0x Public', ROLLBAR_ACCESS_TOKEN: 'a6619002b51c4464928201e6ea94de65', @@ -38,6 +40,7 @@ export const constants = { UNAVAILABLE_STATUS: 503, TAKER_FEE: new BigNumber(0), TESTNET_NAME: 'Kovan', + NUMERAL_USD_FORMAT: '$0,0.00', PROJECT_URL_ETHFINEX: 'https://www.ethfinex.com/', PROJECT_URL_AMADEUS: 'http://amadeusrelay.org', PROJECT_URL_DDEX: 'https://ddex.io', diff --git a/packages/website/ts/utils/utils.ts b/packages/website/ts/utils/utils.ts index 726e1815fc..2cda415450 100644 --- a/packages/website/ts/utils/utils.ts +++ b/packages/website/ts/utils/utils.ts @@ -8,6 +8,8 @@ import * as bowser from 'bowser'; import deepEqual = require('deep-equal'); import * as _ from 'lodash'; import * as moment from 'moment'; +import * as numeral from 'numeral'; + import { AccountState, BlockchainCallErrs, @@ -27,9 +29,6 @@ import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; import * as u2f from 'ts/vendor/u2f_api'; -const LG_MIN_EM = 64; -const MD_MIN_EM = 52; - const isDogfood = (): boolean => _.includes(window.location.href, configs.DOMAIN_DOGFOOD); export const utils = { @@ -134,9 +133,9 @@ export const utils = { // This logic mirrors the CSS media queries in BassCSS for the `lg-`, `md-` and `sm-` CSS // class prefixes. Do not edit these. - if (widthInEm > LG_MIN_EM) { + if (widthInEm > ScreenWidths.Lg) { return ScreenWidths.Lg; - } else if (widthInEm > MD_MIN_EM) { + } else if (widthInEm > ScreenWidths.Md) { return ScreenWidths.Md; } else { return ScreenWidths.Sm; @@ -324,6 +323,7 @@ export const utils = { getProviderType(provider: Provider): Providers | string { const constructorName = provider.constructor.name; let parsedProviderName = constructorName; + // https://ethereum.stackexchange.com/questions/24266/elegant-way-to-detect-current-provider-int-web3-js switch (constructorName) { case 'EthereumProvider': parsedProviderName = Providers.Mist; @@ -337,6 +337,10 @@ export const utils = { parsedProviderName = Providers.Parity; } else if ((provider as any).isMetaMask) { parsedProviderName = Providers.Metamask; + } else if (!_.isUndefined(_.get(window, 'SOFA'))) { + parsedProviderName = Providers.Toshi; + } else if (!_.isUndefined(_.get(window, '__CIPHER__'))) { + parsedProviderName = Providers.Cipher; } return parsedProviderName; }, @@ -380,10 +384,20 @@ export const utils = { }, getFormattedAmount(amount: BigNumber, decimals: number, symbol: string): string { const unitAmount = Web3Wrapper.toUnitAmount(amount, decimals); - const precision = Math.min(constants.TOKEN_AMOUNT_DISPLAY_PRECISION, unitAmount.decimalPlaces()); - const formattedAmount = unitAmount.toFixed(precision); + // if the unit amount is less than 1, show the natural number of decimal places with a max of 4 + // if the unit amount is greater than or equal to 1, show only 2 decimal places + const precision = unitAmount.lt(1) + ? Math.min(constants.TOKEN_AMOUNT_DISPLAY_PRECISION, unitAmount.decimalPlaces()) + : 2; + const format = `0,0.${_.repeat('0', precision)}`; + const formattedAmount = numeral(unitAmount).format(format); return `${formattedAmount} ${symbol}`; }, + getUsdValueFormattedAmount(amount: BigNumber, decimals: number, price: BigNumber): string { + const unitAmount = Web3Wrapper.toUnitAmount(amount, decimals); + const value = unitAmount.mul(price); + return numeral(value).format(constants.NUMERAL_USD_FORMAT); + }, openUrl(url: string): void { window.open(url, '_blank'); }, diff --git a/yarn.lock b/yarn.lock index b44f45c0f8..548859767c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -330,7 +330,7 @@ "@types/mocha@^5.2.2": version "5.2.3" - resolved "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.3.tgz#11f3a5629d67cd444fa6c94536576244e6a52ea9" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.3.tgz#11f3a5629d67cd444fa6c94536576244e6a52ea9" "@types/nock@^9.1.2": version "9.1.3" @@ -350,6 +350,10 @@ version "8.10.8" resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.8.tgz#794cba23cc9f8d9715f6543fa8827433b5f5cd3b" +"@types/numeral@^0.0.22": + version "0.0.22" + resolved "https://registry.yarnpkg.com/@types/numeral/-/numeral-0.0.22.tgz#86bef1f0a2d743afdc2ef3168d45f2905e1a0b93" + "@types/opn@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@types/opn/-/opn-5.1.0.tgz#bff7bc371677f4bdbb37884400e03fd81f743927" @@ -1604,6 +1608,12 @@ base-x@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/base-x/-/base-x-1.1.0.tgz#42d3d717474f9ea02207f6d1aa1f426913eeb7ac" +base-x@^3.0.2: + version "3.0.4" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.4.tgz#94c1788736da065edb1d68808869e357c977fa77" + dependencies: + safe-buffer "^5.0.1" + base64-js@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-0.0.8.tgz#1101e9544f4a76b1bc3b26d452ca96d7a35e7978" @@ -1756,6 +1766,24 @@ bip66@^1.1.3: dependencies: safe-buffer "^5.0.1" +bitcore-lib@^0.15.0: + version "0.15.0" + resolved "https://registry.yarnpkg.com/bitcore-lib/-/bitcore-lib-0.15.0.tgz#f924be13869f2aab7e04aeec5642ad3359b6cec2" + dependencies: + bn.js "=4.11.8" + bs58 "=4.0.1" + buffer-compare "=1.1.1" + elliptic "=6.4.0" + inherits "=2.0.1" + lodash "=4.17.4" + +bitcore-mnemonic@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bitcore-mnemonic/-/bitcore-mnemonic-1.5.0.tgz#c7e785beb6bf0616ed4992785dc3658670425a39" + dependencies: + bitcore-lib "^0.15.0" + unorm "^1.3.3" + bl@^1.0.0: version "1.2.2" resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c" @@ -1801,10 +1829,14 @@ bn.js@4.11.7: version "4.11.7" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.7.tgz#ddb048e50d9482790094c13eb3fcfc833ce7ab46" -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.10.0, bn.js@^4.11.0, bn.js@^4.11.3, bn.js@^4.11.6, bn.js@^4.11.8, bn.js@^4.4.0, bn.js@^4.8.0: +bn.js@=4.11.8, bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.10.0, bn.js@^4.11.0, bn.js@^4.11.3, bn.js@^4.11.6, bn.js@^4.11.8, bn.js@^4.4.0, bn.js@^4.8.0: version "4.11.8" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" +bn.js@^2.0.3: + version "2.2.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-2.2.0.tgz#12162bc2ae71fc40a5626c33438f3a875cd37625" + body-parser@1.18.2, body-parser@^1.16.0, body-parser@^1.17.1: version "1.18.2" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.2.tgz#87678a19d84b47d859b83199bd59bce222b10454" @@ -1975,6 +2007,12 @@ browserslist@^2.1.2: caniuse-lite "^1.0.30000792" electron-to-chromium "^1.3.30" +bs58@=4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" + dependencies: + base-x "^3.0.2" + bs58@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/bs58/-/bs58-2.0.1.tgz#55908d58f1982aba2008fa1bed8f91998a29bf8d" @@ -1996,6 +2034,10 @@ btoa@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/btoa/-/btoa-1.1.2.tgz#3e40b81663f81d2dd6596a4cb714a8dc16cfabe0" +buffer-compare@=1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-compare/-/buffer-compare-1.1.1.tgz#5be7be853af89198d1f4ddc090d1d66a48aef596" + buffer-crc32@~0.2.3: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" @@ -2032,7 +2074,7 @@ buffer@^3.0.1: ieee754 "^1.1.4" isarray "^1.0.0" -buffer@^4.3.0: +buffer@^4.3.0, buffer@^4.9.0: version "4.9.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" dependencies: @@ -2899,7 +2941,7 @@ copyfiles@^1.2.0: copyfiles@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/copyfiles/-/copyfiles-2.0.0.tgz#bbd78bb78e8fd6db5c67adf54249317b24560f2a" + resolved "https://registry.yarnpkg.com/copyfiles/-/copyfiles-2.0.0.tgz#bbd78bb78e8fd6db5c67adf54249317b24560f2a" dependencies: glob "^7.0.5" minimatch "^3.0.3" @@ -3054,7 +3096,7 @@ crypto-js@3.1.9-1, crypto-js@^3.1.9-1: version "3.1.9-1" resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.1.9-1.tgz#fda19e761fc077e01ffbfdc6e9fdfc59e8806cd8" -crypto-js@^3.1.4: +crypto-js@^3.1.4, crypto-js@^3.1.5: version "3.1.8" resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.1.8.tgz#715f070bf6014f2ae992a98b3929258b713f08d5" @@ -3716,7 +3758,7 @@ elliptic@6.3.3: hash.js "^1.0.0" inherits "^2.0.1" -elliptic@^6.0.0, elliptic@^6.2.3, elliptic@^6.4.0: +elliptic@=6.4.0, elliptic@^6.0.0, elliptic@^6.2.3, elliptic@^6.4.0: version "6.4.0" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df" dependencies: @@ -3728,6 +3770,15 @@ elliptic@^6.0.0, elliptic@^6.2.3, elliptic@^6.4.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.0" +elliptic@^3.1.0: + version "3.1.0" + resolved "http://registry.npmjs.org/elliptic/-/elliptic-3.1.0.tgz#c21682ef762769b56a74201609105da11d5f60cc" + dependencies: + bn.js "^2.0.3" + brorand "^1.0.1" + hash.js "^1.0.0" + inherits "^2.0.1" + emojis-list@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" @@ -3954,7 +4005,7 @@ etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" -eth-block-tracker@^2.2.2, eth-block-tracker@^2.3.0: +eth-block-tracker@^2.2.2: version "2.3.0" resolved "https://registry.yarnpkg.com/eth-block-tracker/-/eth-block-tracker-2.3.0.tgz#4cb782c8ef8fde2f5dc894921ae1f5c1077c35a4" dependencies: @@ -4027,6 +4078,22 @@ eth-lib@0.2.7: elliptic "^6.4.0" xhr-request-promise "^0.1.2" +eth-lightwallet@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/eth-lightwallet/-/eth-lightwallet-3.0.1.tgz#297022932aa568f4e4eb0873bff257f5e5b78709" + dependencies: + bitcore-lib "^0.15.0" + bitcore-mnemonic "^1.5.0" + buffer "^4.9.0" + crypto-js "^3.1.5" + elliptic "^3.1.0" + ethereumjs-tx "^1.3.3" + ethereumjs-util "^5.1.1" + rlp "^2.0.0" + scrypt-async "^1.2.0" + tweetnacl "0.13.2" + web3 "0.20.2" + eth-query@^2.0.2, eth-query@^2.1.0, eth-query@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/eth-query/-/eth-query-2.1.2.tgz#d6741d9000106b51510c72db92d6365456a6da5e" @@ -4071,13 +4138,6 @@ ethereum-common@^0.0.18: version "0.0.18" resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.0.18.tgz#2fdc3576f232903358976eb39da783213ff9523f" -ethereum-types@^0.0.2: - version "0.0.2" - resolved "https://registry.npmjs.org/ethereum-types/-/ethereum-types-0.0.2.tgz#6ef6faf46a24697cbf66b6c8a0ecf2095ce58c38" - dependencies: - "@types/node" "^8.0.53" - bignumber.js "~4.1.0" - ethereumjs-abi@0.6.5, ethereumjs-abi@^0.6.4, "ethereumjs-abi@git+https://github.com/ethereumjs/ethereumjs-abi.git": version "0.6.5" resolved "git+https://github.com/ethereumjs/ethereumjs-abi.git#4ea2fdfed09e8f99117d9362d17c6b01b64a2bcf" @@ -4112,13 +4172,13 @@ ethereumjs-block@~1.2.2: ethereumjs-util "^4.0.1" merkle-patricia-tree "^2.1.2" -ethereumjs-blockstream@^2.0.6: - version "2.0.7" - resolved "https://registry.yarnpkg.com/ethereumjs-blockstream/-/ethereumjs-blockstream-2.0.7.tgz#8e791d18d517f13e0ba928892dda545dc930936b" +ethereumjs-blockstream@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ethereumjs-blockstream/-/ethereumjs-blockstream-5.0.0.tgz#63bfe9185757329a32822d5815562eb1dcd75d71" dependencies: - immutable "3.8.1" - source-map-support "0.4.14" - uuid "3.0.1" + immutable "3.8.2" + source-map-support "0.5.6" + uuid "3.2.1" ethereumjs-tx@0xProject/ethereumjs-tx#fake-tx-include-signature-by-default: version "1.3.4" @@ -5803,9 +5863,9 @@ immediate@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.2.3.tgz#d140fa8f614659bd6541233097ddaac25cdd991c" -immutable@3.8.1: - version "3.8.1" - resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.1.tgz#200807f11ab0f72710ea485542de088075f68cd2" +immutable@3.8.2: + version "3.8.2" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3" import-lazy@^2.1.0: version "2.1.0" @@ -5862,7 +5922,7 @@ inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, i version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" -inherits@2.0.1: +inherits@2.0.1, inherits@=2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" @@ -7207,7 +7267,7 @@ lodash@4.17.2: version "4.17.2" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.2.tgz#34a3055babe04ce42467b607d700072c7ff6bf42" -lodash@4.17.4: +lodash@4.17.4, lodash@=4.17.4: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" @@ -8126,6 +8186,10 @@ number-to-bn@1.7.0: bn.js "4.11.6" strip-hex-prefix "1.0.0" +numeral@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/numeral/-/numeral-2.0.6.tgz#4ad080936d443c2561aed9f2197efffe25f4e506" + nyc@^11.0.1: version "11.7.1" resolved "https://registry.yarnpkg.com/nyc/-/nyc-11.7.1.tgz#7cb0a422e501b88ff2c1634341dec2560299d67b" @@ -10215,7 +10279,7 @@ run-queue@^1.0.0, run-queue@^1.0.3: run-s@^0.0.0: version "0.0.0" - resolved "https://registry.npmjs.org/run-s/-/run-s-0.0.0.tgz#599912be20c00ba7698655c9936d075d31b71754" + resolved "https://registry.yarnpkg.com/run-s/-/run-s-0.0.0.tgz#599912be20c00ba7698655c9936d075d31b71754" rustbn.js@~0.1.1: version "0.1.2" @@ -10283,6 +10347,10 @@ scoped-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/scoped-regex/-/scoped-regex-1.0.0.tgz#a346bb1acd4207ae70bd7c0c7ca9e566b6baddb8" +scrypt-async@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/scrypt-async/-/scrypt-async-1.3.1.tgz#a11fd6fac981b4b823ee01dee0221169500ddae9" + scrypt-js@2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.3.tgz#bb0040be03043da9a012a2cea9fc9f852cfc87d4" @@ -10756,11 +10824,12 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@0.4.14: - version "0.4.14" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.14.tgz#9d4463772598b86271b4f523f6c1f4e02a7d6aef" +source-map-support@0.5.6, source-map-support@^0.5.6: + version "0.5.6" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.6.tgz#4435cee46b1aab62b8e8610ce60f788091c51c13" dependencies: - source-map "^0.5.6" + buffer-from "^1.0.0" + source-map "^0.6.0" source-map-support@^0.4.15: version "0.4.18" @@ -10774,13 +10843,6 @@ source-map-support@^0.5.0, source-map-support@^0.5.3: dependencies: source-map "^0.6.0" -source-map-support@^0.5.6: - version "0.5.6" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.6.tgz#4435cee46b1aab62b8e8610ce60f788091c51c13" - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - source-map-url@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" @@ -11735,6 +11797,10 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" +tweetnacl@0.13.2: + version "0.13.2" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.13.2.tgz#453161770469d45cd266c36404e2bc99a8fa9944" + tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" @@ -12187,14 +12253,14 @@ uuid@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" +uuid@3.2.1, uuid@^3.0.0, uuid@^3.0.1, uuid@^3.1.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14" + uuid@^2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" -uuid@^3.0.0, uuid@^3.0.1, uuid@^3.1.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14" - uvm@1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/uvm/-/uvm-1.7.0.tgz#685d3a149ec7118fb73a73dfdc158ab46b0f0634" @@ -12535,57 +12601,7 @@ web3-net@1.0.0-beta.34: web3-core-method "1.0.0-beta.34" web3-utils "1.0.0-beta.34" -web3-provider-engine@^13.3.2: - version "13.8.0" - resolved "https://registry.yarnpkg.com/web3-provider-engine/-/web3-provider-engine-13.8.0.tgz#4c7c1ad2af5f1fe10343b8a65495879a2f9c00df" - dependencies: - async "^2.5.0" - clone "^2.0.0" - eth-block-tracker "^2.2.2" - eth-sig-util "^1.4.2" - ethereumjs-block "^1.2.2" - ethereumjs-tx "^1.2.0" - ethereumjs-util "^5.1.1" - ethereumjs-vm "^2.0.2" - fetch-ponyfill "^4.0.0" - json-rpc-error "^2.0.0" - json-stable-stringify "^1.0.1" - promise-to-callback "^1.0.0" - readable-stream "^2.2.9" - request "^2.67.0" - semaphore "^1.0.3" - solc "^0.4.2" - tape "^4.4.0" - xhr "^2.2.0" - xtend "^4.0.1" - -web3-provider-engine@^14.0.4: - version "14.0.4" - resolved "https://registry.yarnpkg.com/web3-provider-engine/-/web3-provider-engine-14.0.4.tgz#6f96b71ea1b3a76cc67cd52007116c8d4b64465b" - dependencies: - async "^2.5.0" - backoff "^2.5.0" - clone "^2.0.0" - cross-fetch "^2.1.0" - eth-block-tracker "^2.3.0" - eth-json-rpc-infura "^3.1.0" - eth-sig-util "^1.4.2" - ethereumjs-block "^1.2.2" - ethereumjs-tx "^1.2.0" - ethereumjs-util "^5.1.5" - ethereumjs-vm "^2.3.4" - json-rpc-error "^2.0.0" - json-stable-stringify "^1.0.1" - promise-to-callback "^1.0.0" - readable-stream "^2.2.9" - request "^2.67.0" - semaphore "^1.0.3" - tape "^4.4.0" - ws "^5.1.1" - xhr "^2.2.0" - xtend "^4.0.1" - -web3-provider-engine@^14.0.6: +web3-provider-engine@14.0.6, web3-provider-engine@^14.0.6: version "14.0.6" resolved "https://registry.yarnpkg.com/web3-provider-engine/-/web3-provider-engine-14.0.6.tgz#cbdd66fe20c0136a3a495cbe40d18b6c4160d5f0" dependencies: @@ -12611,6 +12627,30 @@ web3-provider-engine@^14.0.6: xhr "^2.2.0" xtend "^4.0.1" +web3-provider-engine@^13.3.2: + version "13.8.0" + resolved "https://registry.yarnpkg.com/web3-provider-engine/-/web3-provider-engine-13.8.0.tgz#4c7c1ad2af5f1fe10343b8a65495879a2f9c00df" + dependencies: + async "^2.5.0" + clone "^2.0.0" + eth-block-tracker "^2.2.2" + eth-sig-util "^1.4.2" + ethereumjs-block "^1.2.2" + ethereumjs-tx "^1.2.0" + ethereumjs-util "^5.1.1" + ethereumjs-vm "^2.0.2" + fetch-ponyfill "^4.0.0" + json-rpc-error "^2.0.0" + json-stable-stringify "^1.0.1" + promise-to-callback "^1.0.0" + readable-stream "^2.2.9" + request "^2.67.0" + semaphore "^1.0.3" + solc "^0.4.2" + tape "^4.4.0" + xhr "^2.2.0" + xtend "^4.0.1" + web3-providers-http@1.0.0-beta.34: version "1.0.0-beta.34" resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.0.0-beta.34.tgz#e561b52bbb43766282007d40285bfe3550c27e7a" @@ -12661,6 +12701,16 @@ web3-utils@1.0.0-beta.34, web3-utils@^1.0.0-beta.34: underscore "1.8.3" utf8 "2.1.1" +web3@0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/web3/-/web3-0.20.2.tgz#c54dac5fc0e377399c04c1a6ecbb12e4513278d6" + dependencies: + bignumber.js "git+https://github.com/frozeman/bignumber.js-nolookahead.git" + crypto-js "^3.1.4" + utf8 "^2.1.1" + xhr2 "*" + xmlhttprequest "*" + web3@^0.18.0: version "0.18.4" resolved "https://registry.yarnpkg.com/web3/-/web3-0.18.4.tgz#81ec1784145491f2eaa8955b31c06049e07c5e7d" @@ -13168,7 +13218,7 @@ yargs@^10.0.3: yargs@^11.0.0: version "11.0.0" - resolved "https://registry.npmjs.org/yargs/-/yargs-11.0.0.tgz#c052931006c5eee74610e5fc0354bedfd08a201b" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.0.0.tgz#c052931006c5eee74610e5fc0354bedfd08a201b" dependencies: cliui "^4.0.0" decamelize "^1.1.1"