diff options
122 files changed, 3478 insertions, 1393 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml index 31954c608..b50039aa3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -172,7 +172,7 @@ jobs: - run: yarn lerna run lint - run: yarn prettier:ci - run: cd packages/0x.js && yarn build:umd:prod - - run: yarn bundlesize + - run: yarn bundlewatch submit-coverage: docker: - image: circleci/node:9 diff --git a/.gitignore b/.gitignore index dd022825d..1ae1b412d 100644 --- a/.gitignore +++ b/.gitignore @@ -72,6 +72,7 @@ TODO.md .vscode packages/website/public/bundle* +packages/dev-tools-pages/public/bundle* packages/react-docs/example/public/bundle* # server cli diff --git a/package.json b/package.json index 8eb736cc4..920afaf29 100644 --- a/package.json +++ b/package.json @@ -36,27 +36,35 @@ "test": "wsrun test $PKG --fast-exit --serial --exclude-missing", "generate_doc": "node ./packages/monorepo-scripts/lib/doc_generate_and_upload.js", "test:generate_docs:circleci": "for i in ${npm_package_config_packagesWithDocPages}; do yarn generate_doc --package $i --shouldUpload false --isStaging true || break -1; done;", - "bundlesize": "bundlesize", + "bundlewatch": "bundlewatch", "lint": "wsrun lint $PKG --fast-exit --parallel --exclude-missing" }, "config": { "mnemonic": "concert load couple harbor equip island argue ramp clarify fence smart topic", "packagesWithDocPages": "0x.js connect json-schemas subproviders web3-wrapper contract-wrappers order-utils order-watcher sol-compiler sol-cov ethereum-types" }, - "bundlesize": [ - { - "path": "packages/0x.js/_bundles/index.min.js" - }, - { - "path": "packages/instant/public/main.bundle.js" + "bundlewatch" : { + "files": [ + { + "path": "packages/0x.js/_bundles/index.min.js", + "maxSize": "700kB" + }, + { + "path": "packages/instant/public/main.bundle.js", + "maxSize": "350kB" + } + ], + "ci": { + "trackBranches": ["master", "development"], + "repoBranchBase": "development" } - ], + }, "devDependencies": { "@0x-lerna-fork/lerna": "3.0.0-beta.25", "async-child-process": "^1.1.1", - "bundlesize": "^0.17.0", + "bundlewatch": "^0.2.1", "coveralls": "^3.0.0", - "ganache-cli": "6.1.3", + "ganache-cli": "6.1.8", "lcov-result-merger": "^3.0.0", "npm-cli-login": "^0.0.10", "npm-run-all": "^4.1.2", diff --git a/packages/0x.js/CHANGELOG.json b/packages/0x.js/CHANGELOG.json index 1d6f08760..6dfcc3d33 100644 --- a/packages/0x.js/CHANGELOG.json +++ b/packages/0x.js/CHANGELOG.json @@ -1,5 +1,24 @@ [ { + "version": "2.0.0", + "changes": [ + { + "note": "Add support for `eth_signTypedData`.", + "pr": 1102 + }, + { + "note": + "Added `MetamaskSubprovider` to handle inconsistencies in Metamask's signing JSON RPC endpoints.", + "pr": 1102 + }, + { + "note": + "Removed `SignerType` (including `SignerType.Metamask`). Please use the `MetamaskSubprovider` to wrap `web3.currentProvider`.", + "pr": 1102 + } + ] + }, + { "version": "1.0.8", "changes": [ { diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json index 6a3074d26..1a57edd45 100644 --- a/packages/0x.js/package.json +++ b/packages/0x.js/package.json @@ -84,7 +84,7 @@ "@0xproject/utils": "^2.0.2", "@0xproject/web3-wrapper": "^3.0.3", "ethereum-types": "^1.0.11", - "ethers": "4.0.0-beta.14", + "ethers": "~4.0.4", "lodash": "^4.17.5", "web3-provider-engine": "14.0.6" }, diff --git a/packages/0x.js/src/index.ts b/packages/0x.js/src/index.ts index d07bfcfc8..6eb1fd8ee 100644 --- a/packages/0x.js/src/index.ts +++ b/packages/0x.js/src/index.ts @@ -53,7 +53,13 @@ export { OrderWatcher, OnOrderStateChangeCallback, OrderWatcherConfig } from '@0 export import Web3ProviderEngine = require('web3-provider-engine'); -export { RPCSubprovider, Callback, JSONRPCRequestPayloadWithMethod, ErrorCallback } from '@0xproject/subproviders'; +export { + RPCSubprovider, + Callback, + JSONRPCRequestPayloadWithMethod, + ErrorCallback, + MetamaskSubprovider, +} from '@0xproject/subproviders'; export { AbiDecoder } from '@0xproject/utils'; @@ -68,7 +74,6 @@ export { OrderStateInvalid, OrderState, AssetProxyId, - SignerType, ERC20AssetData, ERC721AssetData, SignatureType, @@ -85,6 +90,7 @@ export { JSONRPCRequestPayload, JSONRPCResponsePayload, JSONRPCErrorCallback, + JSONRPCResponseError, LogEntry, DecodedLogArgs, LogEntryEvent, diff --git a/packages/asset-buyer/CHANGELOG.json b/packages/asset-buyer/CHANGELOG.json index d50a564dc..dbb801b69 100644 --- a/packages/asset-buyer/CHANGELOG.json +++ b/packages/asset-buyer/CHANGELOG.json @@ -1,5 +1,13 @@ [ { + "version": "2.1.0", + "changes": [ + { + "note": "Add `gasLimit` and `gasPrice` as optional properties on `BuyQuoteExecutionOpts`" + } + ] + }, + { "version": "2.0.0", "changes": [ { diff --git a/packages/asset-buyer/src/asset_buyer.ts b/packages/asset-buyer/src/asset_buyer.ts index 7ec39e012..50343efde 100644 --- a/packages/asset-buyer/src/asset_buyer.ts +++ b/packages/asset-buyer/src/asset_buyer.ts @@ -183,7 +183,7 @@ export class AssetBuyer { buyQuote: BuyQuote, options: Partial<BuyQuoteExecutionOpts> = {}, ): Promise<string> { - const { ethAmount, takerAddress, feeRecipient } = { + const { ethAmount, takerAddress, feeRecipient, gasLimit, gasPrice } = { ...constants.DEFAULT_BUY_QUOTE_EXECUTION_OPTS, ...options, }; @@ -219,6 +219,10 @@ export class AssetBuyer { feeOrders, feePercentage, feeRecipient, + { + gasLimit, + gasPrice, + }, ); return txHash; } diff --git a/packages/asset-buyer/src/constants.ts b/packages/asset-buyer/src/constants.ts index e1056e39b..e095dee06 100644 --- a/packages/asset-buyer/src/constants.ts +++ b/packages/asset-buyer/src/constants.ts @@ -8,7 +8,7 @@ const MAINNET_NETWORK_ID = 1; const DEFAULT_ASSET_BUYER_OPTS: AssetBuyerOpts = { networkId: MAINNET_NETWORK_ID, orderRefreshIntervalMs: 10000, // 10 seconds - expiryBufferSeconds: 15, + expiryBufferSeconds: 300, // 5 minutes }; const DEFAULT_BUY_QUOTE_REQUEST_OPTS: BuyQuoteRequestOpts = { diff --git a/packages/asset-buyer/src/types.ts b/packages/asset-buyer/src/types.ts index b96795bb6..6218f4ba4 100644 --- a/packages/asset-buyer/src/types.ts +++ b/packages/asset-buyer/src/types.ts @@ -77,18 +77,22 @@ export interface BuyQuoteRequestOpts { /** * ethAmount: The desired amount of eth to spend. Defaults to buyQuote.worstCaseQuoteInfo.totalEthAmount. * takerAddress: The address to perform the buy. Defaults to the first available address from the provider. + * gasLimit: The amount of gas to send with a transaction (in Gwei). Defaults to an eth_estimateGas rpc call. + * gasPrice: Gas price in Wei to use for a transaction * feeRecipient: The address where affiliate fees are sent. Defaults to null address (0x000...000). */ export interface BuyQuoteExecutionOpts { ethAmount?: BigNumber; takerAddress?: string; + gasLimit?: number; + gasPrice?: BigNumber; feeRecipient: string; } /** * networkId: The ethereum network id. Defaults to 1 (mainnet). * orderRefreshIntervalMs: The interval in ms that getBuyQuoteAsync should trigger an refresh of orders and order states. Defaults to 10000ms (10s). - * expiryBufferSeconds: The number of seconds to add when calculating whether an order is expired or not. Defaults to 15s. + * expiryBufferSeconds: The number of seconds to add when calculating whether an order is expired or not. Defaults to 300s (5m). */ export interface AssetBuyerOpts { networkId: number; diff --git a/packages/asset-buyer/src/utils/buy_quote_calculator.ts b/packages/asset-buyer/src/utils/buy_quote_calculator.ts index cb0fd128c..a1d334eef 100644 --- a/packages/asset-buyer/src/utils/buy_quote_calculator.ts +++ b/packages/asset-buyer/src/utils/buy_quote_calculator.ts @@ -72,7 +72,6 @@ export const buyQuoteCalculator = { assetBuyAmount, feePercentage, ); - return { assetData, orders: resultOrders, @@ -98,13 +97,14 @@ function calculateQuoteInfo( ); // find the total eth needed to buy fees const ethAmountToBuyFees = findEthAmountNeededToBuyFees(feeOrdersAndFillableAmounts, zrxAmountToBuyAsset); - const ethAmountBeforeAffiliateFee = ethAmountToBuyAsset.plus(ethAmountToBuyFees); - const totalEthAmount = ethAmountBeforeAffiliateFee.mul(feePercentage + 1); + const affiliateFeeEthAmount = ethAmountToBuyAsset.mul(feePercentage); + const totalEthAmountWithoutAffiliateFee = ethAmountToBuyAsset.plus(ethAmountToBuyFees); + const totalEthAmount = totalEthAmountWithoutAffiliateFee.plus(affiliateFeeEthAmount); // divide into the assetBuyAmount in order to find rate of makerAsset / WETH - const ethPerAssetPrice = ethAmountBeforeAffiliateFee.div(assetBuyAmount); + const ethPerAssetPrice = totalEthAmountWithoutAffiliateFee.div(assetBuyAmount); return { totalEthAmount, - feeEthAmount: totalEthAmount.minus(ethAmountBeforeAffiliateFee), + feeEthAmount: affiliateFeeEthAmount, ethPerAssetPrice, }; } diff --git a/packages/asset-buyer/src/utils/order_utils.ts b/packages/asset-buyer/src/utils/order_utils.ts index 62166eb76..cfc13a8a1 100644 --- a/packages/asset-buyer/src/utils/order_utils.ts +++ b/packages/asset-buyer/src/utils/order_utils.ts @@ -10,7 +10,7 @@ export const orderUtils = { willOrderExpire(order: SignedOrder, secondsFromNow: number): boolean { const millisecondsInSecond = 1000; const currentUnixTimestampSec = new BigNumber(Date.now() / millisecondsInSecond).round(); - return order.expirationTimeSeconds.lessThan(currentUnixTimestampSec.minus(secondsFromNow)); + return order.expirationTimeSeconds.lessThan(currentUnixTimestampSec.plus(secondsFromNow)); }, calculateRemainingMakerAssetAmount(order: SignedOrder, remainingTakerAssetAmount: BigNumber): BigNumber { if (remainingTakerAssetAmount.eq(0)) { diff --git a/packages/asset-buyer/test/buy_quote_calculator_test.ts b/packages/asset-buyer/test/buy_quote_calculator_test.ts index b987b45a8..fda6958cd 100644 --- a/packages/asset-buyer/test/buy_quote_calculator_test.ts +++ b/packages/asset-buyer/test/buy_quote_calculator_test.ts @@ -103,9 +103,11 @@ describe('buyQuoteCalculator', () => { expect(buyQuote.feeOrders).to.deep.equal([smallFeeOrderAndFillableAmount.orders[0]]); // test if rates are correct // 50 eth to fill the first order + 100 eth for fees - const expectedFillEthAmount = new BigNumber(150); - const expectedTotalEthAmount = expectedFillEthAmount.mul(feePercentage + 1); - const expectedFeeEthAmount = expectedTotalEthAmount.minus(expectedFillEthAmount); + const expectedEthAmountForAsset = new BigNumber(50); + const expectedEthAmountForZrxFees = new BigNumber(100); + const expectedFillEthAmount = expectedEthAmountForAsset.plus(expectedEthAmountForZrxFees); + const expectedFeeEthAmount = expectedEthAmountForAsset.mul(feePercentage); + const expectedTotalEthAmount = expectedFillEthAmount.plus(expectedFeeEthAmount); const expectedEthPerAssetPrice = expectedFillEthAmount.div(assetBuyAmount); expect(buyQuote.bestCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedFeeEthAmount); expect(buyQuote.bestCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedTotalEthAmount); @@ -138,17 +140,21 @@ describe('buyQuoteCalculator', () => { expect(buyQuote.feeOrders).to.deep.equal(allFeeOrdersAndFillableAmounts.orders); // test if rates are correct // 50 eth to fill the first order + 100 eth for fees - const expectedFillEthAmount = new BigNumber(150); - const expectedTotalEthAmount = expectedFillEthAmount.mul(feePercentage + 1); - const expectedFeeEthAmount = expectedTotalEthAmount.minus(expectedFillEthAmount); + const expectedEthAmountForAsset = new BigNumber(50); + const expectedEthAmountForZrxFees = new BigNumber(100); + const expectedFillEthAmount = expectedEthAmountForAsset.plus(expectedEthAmountForZrxFees); + const expectedFeeEthAmount = expectedEthAmountForAsset.mul(feePercentage); + const expectedTotalEthAmount = expectedFillEthAmount.plus(expectedFeeEthAmount); const expectedEthPerAssetPrice = expectedFillEthAmount.div(assetBuyAmount); expect(buyQuote.bestCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedFeeEthAmount); expect(buyQuote.bestCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedTotalEthAmount); expect(buyQuote.bestCaseQuoteInfo.ethPerAssetPrice).to.bignumber.equal(expectedEthPerAssetPrice); // 100 eth to fill the first order + 200 eth for fees - const expectedWorstFillEthAmount = new BigNumber(300); - const expectedWorstTotalEthAmount = expectedWorstFillEthAmount.mul(feePercentage + 1); - const expectedWorstFeeEthAmount = expectedWorstTotalEthAmount.minus(expectedWorstFillEthAmount); + const expectedWorstEthAmountForAsset = new BigNumber(100); + const expectedWorstEthAmountForZrxFees = new BigNumber(200); + const expectedWorstFillEthAmount = expectedWorstEthAmountForAsset.plus(expectedWorstEthAmountForZrxFees); + const expectedWorstFeeEthAmount = expectedWorstEthAmountForAsset.mul(feePercentage); + const expectedWorstTotalEthAmount = expectedWorstFillEthAmount.plus(expectedWorstFeeEthAmount); const expectedWorstEthPerAssetPrice = expectedWorstFillEthAmount.div(assetBuyAmount); expect(buyQuote.worstCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedWorstFeeEthAmount); expect(buyQuote.worstCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedWorstTotalEthAmount); diff --git a/packages/base-contract/package.json b/packages/base-contract/package.json index 7aac46ab9..4ee471219 100644 --- a/packages/base-contract/package.json +++ b/packages/base-contract/package.json @@ -45,7 +45,7 @@ "@0xproject/utils": "^2.0.2", "@0xproject/web3-wrapper": "^3.0.3", "ethereum-types": "^1.0.11", - "ethers": "4.0.0-beta.14", + "ethers": "~4.0.4", "lodash": "^4.17.5" }, "publishConfig": { diff --git a/packages/base-contract/src/index.ts b/packages/base-contract/src/index.ts index 981e6fca6..a8e4ad2e2 100644 --- a/packages/base-contract/src/index.ts +++ b/packages/base-contract/src/index.ts @@ -17,7 +17,7 @@ import * as _ from 'lodash'; import { formatABIDataItem } from './utils'; export interface EthersInterfaceByFunctionSignature { - [key: string]: ethers.Interface; + [key: string]: ethers.utils.Interface; } const REVERT_ERROR_SELECTOR = '08c379a0'; @@ -101,7 +101,7 @@ export class BaseContract { // if it overflows the corresponding Solidity type, there is a bug in the // encoder, or the encoder performs unsafe type coercion. public static strictArgumentEncodingCheck(inputAbi: DataItem[], args: any[]): void { - const coder = new ethers.AbiCoder(); + const coder = new ethers.utils.AbiCoder(); const params = abiUtils.parseEthersParams(inputAbi); const rawEncoded = coder.encode(inputAbi, args); const rawDecoded = coder.decode(inputAbi, rawEncoded); @@ -117,7 +117,7 @@ export class BaseContract { } } } - protected _lookupEthersInterface(functionSignature: string): ethers.Interface { + protected _lookupEthersInterface(functionSignature: string): ethers.utils.Interface { const ethersInterface = this._ethersInterfacesByFunctionSignature[functionSignature]; if (_.isUndefined(ethersInterface)) { throw new Error(`Failed to lookup method with function signature '${functionSignature}'`); @@ -154,7 +154,7 @@ export class BaseContract { this._ethersInterfacesByFunctionSignature = {}; _.each(methodAbis, methodAbi => { const functionSignature = abiUtils.getFunctionSignature(methodAbi); - this._ethersInterfacesByFunctionSignature[functionSignature] = new ethers.Interface([methodAbi]); + this._ethersInterfacesByFunctionSignature[functionSignature] = new ethers.utils.Interface([methodAbi]); }); } } diff --git a/packages/contract-wrappers/package.json b/packages/contract-wrappers/package.json index e83caad97..35f27d77c 100644 --- a/packages/contract-wrappers/package.json +++ b/packages/contract-wrappers/package.json @@ -84,7 +84,7 @@ "ethereum-types": "^1.0.11", "ethereumjs-blockstream": "6.0.0", "ethereumjs-util": "^5.1.1", - "ethers": "4.0.0-beta.14", + "ethers": "~4.0.4", "js-sha3": "^0.7.0", "lodash": "^4.17.5", "uuid": "^3.1.0" diff --git a/packages/contract-wrappers/src/index.ts b/packages/contract-wrappers/src/index.ts index 2fcdd2ddb..e8a53170e 100644 --- a/packages/contract-wrappers/src/index.ts +++ b/packages/contract-wrappers/src/index.ts @@ -39,6 +39,7 @@ export { JSONRPCRequestPayload, JSONRPCResponsePayload, JSONRPCErrorCallback, + JSONRPCResponseError, AbiDefinition, LogWithDecodedArgs, FunctionAbi, diff --git a/packages/contract-wrappers/src/utils/transaction_encoder.ts b/packages/contract-wrappers/src/utils/transaction_encoder.ts index 87cbb43fd..33086944b 100644 --- a/packages/contract-wrappers/src/utils/transaction_encoder.ts +++ b/packages/contract-wrappers/src/utils/transaction_encoder.ts @@ -1,22 +1,13 @@ import { schemas } from '@0xproject/json-schemas'; -import { EIP712Schema, EIP712Types, eip712Utils } from '@0xproject/order-utils'; +import { eip712Utils } from '@0xproject/order-utils'; import { Order, SignedOrder } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; +import { BigNumber, signTypedDataUtils } from '@0xproject/utils'; import _ = require('lodash'); import { ExchangeContract } from '../contract_wrappers/generated/exchange'; import { assert } from './assert'; -const EIP712_ZEROEX_TRANSACTION_SCHEMA: EIP712Schema = { - name: 'ZeroExTransaction', - parameters: [ - { name: 'salt', type: EIP712Types.Uint256 }, - { name: 'signerAddress', type: EIP712Types.Address }, - { name: 'data', type: EIP712Types.Bytes }, - ], -}; - /** * Transaction Encoder. Transaction messages exist for the purpose of calling methods on the Exchange contract * in the context of another address. For example, UserA can encode and sign a fillOrder transaction and UserB @@ -41,12 +32,9 @@ export class TransactionEncoder { signerAddress, data, }; - const executeTransactionHashBuff = eip712Utils.structHash( - EIP712_ZEROEX_TRANSACTION_SCHEMA, - executeTransactionData, - ); - const eip721MessageBuffer = eip712Utils.createEIP712Message(executeTransactionHashBuff, exchangeAddress); - const messageHex = `0x${eip721MessageBuffer.toString('hex')}`; + const typedData = eip712Utils.createZeroExTransactionTypedData(executeTransactionData, exchangeAddress); + const eip712MessageBuffer = signTypedDataUtils.generateTypedDataHash(typedData); + const messageHex = `0x${eip712MessageBuffer.toString('hex')}`; return messageHex; } /** diff --git a/packages/contract-wrappers/test/transaction_encoder_test.ts b/packages/contract-wrappers/test/transaction_encoder_test.ts index a397e43a8..9da8fe2ca 100644 --- a/packages/contract-wrappers/test/transaction_encoder_test.ts +++ b/packages/contract-wrappers/test/transaction_encoder_test.ts @@ -1,7 +1,7 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; import { FillScenarios } from '@0xproject/fill-scenarios'; import { assetDataUtils, generatePseudoRandomSalt, orderHashUtils, signatureUtils } from '@0xproject/order-utils'; -import { SignedOrder, SignerType } from '@0xproject/types'; +import { SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import 'mocha'; @@ -80,12 +80,7 @@ describe('TransactionEncoder', () => { ): Promise<void> => { const salt = generatePseudoRandomSalt(); const encodedTransaction = encoder.getTransactionHex(data, salt, signerAddress); - const signature = await signatureUtils.ecSignOrderHashAsync( - provider, - encodedTransaction, - signerAddress, - SignerType.Default, - ); + const signature = await signatureUtils.ecSignHashAsync(provider, encodedTransaction, signerAddress); txHash = await contractWrappers.exchange.executeTransactionAsync( salt, signerAddress, diff --git a/packages/contract_templates/contract.handlebars b/packages/contract_templates/contract.handlebars index 9ae39f44f..41e5c8630 100644 --- a/packages/contract_templates/contract.handlebars +++ b/packages/contract_templates/contract.handlebars @@ -65,7 +65,7 @@ export class {{contractName}}Contract extends BaseContract { [{{> params inputs=ctor.inputs}}], BaseContract._bigNumberToString, ); - const iface = new ethers.Interface(abi); + const iface = new ethers.utils.Interface(abi); const deployInfo = iface.deployFunction; const txData = deployInfo.encode(bytecode, [{{> params inputs=ctor.inputs}}]); const web3Wrapper = new Web3Wrapper(provider); diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 081e5f8ee..01d04e645 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -84,7 +84,7 @@ "ethereum-types": "^1.0.11", "ethereumjs-abi": "0.6.5", "ethereumjs-util": "^5.1.1", - "ethers": "4.0.0-beta.14", + "ethers": "~4.0.4", "js-combinatorics": "^0.5.3", "lodash": "^4.17.5" } diff --git a/packages/contracts/test/exchange/libs.ts b/packages/contracts/test/exchange/libs.ts index 37234489e..049b7f37a 100644 --- a/packages/contracts/test/exchange/libs.ts +++ b/packages/contracts/test/exchange/libs.ts @@ -1,5 +1,5 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; -import { assetDataUtils, eip712Utils, orderHashUtils } from '@0xproject/order-utils'; +import { assetDataUtils, orderHashUtils } from '@0xproject/order-utils'; import { SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; @@ -126,22 +126,6 @@ describe('Exchange libs', () => { }); describe('LibOrder', () => { - describe('getOrderSchema', () => { - it('should output the correct order schema hash', async () => { - const orderSchema = await libs.getOrderSchemaHash.callAsync(); - const schemaHashBuffer = orderHashUtils._getOrderSchemaBuffer(); - const schemaHashHex = `0x${schemaHashBuffer.toString('hex')}`; - expect(schemaHashHex).to.be.equal(orderSchema); - }); - }); - describe('getDomainSeparatorSchema', () => { - it('should output the correct domain separator schema hash', async () => { - const domainSeparatorSchema = await libs.getDomainSeparatorSchemaHash.callAsync(); - const domainSchemaBuffer = eip712Utils._getDomainSeparatorSchemaBuffer(); - const schemaHashHex = `0x${domainSchemaBuffer.toString('hex')}`; - expect(schemaHashHex).to.be.equal(domainSeparatorSchema); - }); - }); describe('getOrderHash', () => { it('should output the correct orderHash', async () => { signedOrder = await orderFactory.newSignedOrderAsync(); diff --git a/packages/contracts/test/exchange/signature_validator.ts b/packages/contracts/test/exchange/signature_validator.ts index 5cc62e777..192ed3ca9 100644 --- a/packages/contracts/test/exchange/signature_validator.ts +++ b/packages/contracts/test/exchange/signature_validator.ts @@ -1,6 +1,6 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; import { assetDataUtils, orderHashUtils, signatureUtils } from '@0xproject/order-utils'; -import { RevertReason, SignatureType, SignedOrder, SignerType } from '@0xproject/types'; +import { RevertReason, SignatureType, SignedOrder } from '@0xproject/types'; import * as chai from 'chai'; import { LogWithDecodedArgs } from 'ethereum-types'; import ethUtil = require('ethereumjs-util'); @@ -231,10 +231,7 @@ describe('MixinSignatureValidator', () => { it('should return true when SignatureType=EthSign and signature is valid', async () => { // Create EthSign signature const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); - const orderHashWithEthSignPrefixHex = signatureUtils.addSignedMessagePrefix( - orderHashHex, - SignerType.Default, - ); + const orderHashWithEthSignPrefixHex = signatureUtils.addSignedMessagePrefix(orderHashHex); const orderHashWithEthSignPrefixBuffer = ethUtil.toBuffer(orderHashWithEthSignPrefixHex); const ecSignature = ethUtil.ecsign(orderHashWithEthSignPrefixBuffer, signerPrivateKey); // Create 0x signature from EthSign signature @@ -257,10 +254,7 @@ describe('MixinSignatureValidator', () => { it('should return false when SignatureType=EthSign and signature is invalid', async () => { // Create EthSign signature const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); - const orderHashWithEthSignPrefixHex = signatureUtils.addSignedMessagePrefix( - orderHashHex, - SignerType.Default, - ); + const orderHashWithEthSignPrefixHex = signatureUtils.addSignedMessagePrefix(orderHashHex); const orderHashWithEthSignPrefixBuffer = ethUtil.toBuffer(orderHashWithEthSignPrefixHex); const ecSignature = ethUtil.ecsign(orderHashWithEthSignPrefixBuffer, signerPrivateKey); // Create 0x signature from EthSign signature diff --git a/packages/contracts/test/utils/transaction_factory.ts b/packages/contracts/test/utils/transaction_factory.ts index 8465a6a30..9ed4c5a31 100644 --- a/packages/contracts/test/utils/transaction_factory.ts +++ b/packages/contracts/test/utils/transaction_factory.ts @@ -1,19 +1,11 @@ -import { EIP712Schema, EIP712Types, eip712Utils, generatePseudoRandomSalt } from '@0xproject/order-utils'; +import { eip712Utils, generatePseudoRandomSalt } from '@0xproject/order-utils'; import { SignatureType } from '@0xproject/types'; +import { signTypedDataUtils } from '@0xproject/utils'; import * as ethUtil from 'ethereumjs-util'; import { signingUtils } from './signing_utils'; import { SignedTransaction } from './types'; -const EIP712_ZEROEX_TRANSACTION_SCHEMA: EIP712Schema = { - name: 'ZeroExTransaction', - parameters: [ - { name: 'salt', type: EIP712Types.Uint256 }, - { name: 'signerAddress', type: EIP712Types.Address }, - { name: 'data', type: EIP712Types.Bytes }, - ], -}; - export class TransactionFactory { private readonly _signerBuff: Buffer; private readonly _exchangeAddress: string; @@ -31,12 +23,10 @@ export class TransactionFactory { signerAddress, data, }; - const executeTransactionHashBuff = eip712Utils.structHash( - EIP712_ZEROEX_TRANSACTION_SCHEMA, - executeTransactionData, - ); - const txHash = eip712Utils.createEIP712Message(executeTransactionHashBuff, this._exchangeAddress); - const signature = signingUtils.signMessage(txHash, this._privateKey, signatureType); + + const typedData = eip712Utils.createZeroExTransactionTypedData(executeTransactionData, this._exchangeAddress); + const eip712MessageBuffer = signTypedDataUtils.generateTypedDataHash(typedData); + const signature = signingUtils.signMessage(eip712MessageBuffer, this._privateKey, signatureType); const signedTx = { exchangeAddress: this._exchangeAddress, signature: `0x${signature.toString('hex')}`, diff --git a/packages/dev-tools-pages/README.md b/packages/dev-tools-pages/README.md new file mode 100644 index 000000000..39fe70a25 --- /dev/null +++ b/packages/dev-tools-pages/README.md @@ -0,0 +1,88 @@ +## Dev tools pages + +This repository contains our dev tools pages. + +## Local Dev Setup + +Requires Node version 6.9.5 or higher & yarn v1.9.4 + +### 1. Install dependencies for monorepo: + +Make sure you install Yarn v1.9.4 (npm won't work!). We rely on our `yarn.lock` file and on Yarn's support for `workspaces` in our monorepo setup. + +```bash +yarn install +``` + +### 2. Initial setup + +To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory: + +```bash +PKG=@0xproject/dev-tools-pages yarn build +``` + +Note: Ignore the `WARNING in asset size limit` and `WARNING in entrypoint size limit` warnings. + +### 3. Run dev server + +```bash +cd packages/dev-tools-pages +yarn dev +``` + +Visit [http://localhost:3572/](http://localhost:3572/) in your browser. + +The webpage will refresh when source code is changed. + +### 4. Code! + +There are some basic primitives we'd like you to use: + +1. `<Container>Stuff</Container>`: Use containers instead of divs,spans,etc... and use it's props instead of inline styles (e.g `style={{margin: 3}}` should be `margin="3px"` + +2. `<Text>Look ma, text!</Text>`: Use text components whenever rendering text. It has props for manipulating texts, so again no in-line styles. Use `fontColor="red"`, not `style={{color: 'red'}}`. + +3. Styled-components: See the `ui/button.tsx` file for an example of how to use these. + +4. BassCss: This library gives you access to a bunch of [classes](http://basscss.com/) that apply styles in a browser-compatible way, has affordances for responsiveness and alleviates the need for inline styles or LESS/CSS files. + +With the above 4 tools and following the React paradigm, you shouldn't need CSS/LESS files. IF there are special occasions where you do, these is a `all.less` file, but this is a solution of last resort. Use it sparingly. + +### Clean + +```bash +yarn clean +``` + +### Lint + +```bash +yarn lint +``` + +### Prettier + +Run from the monorepo root directory: + +``` +yarn prettier +``` + +### Resources + +##### Toolkit + +* [Styled Components](https://www.styled-components.com/) +* [BassCSS](http://basscss.com/) + +##### Recommended Atom packages: + +* [atom-typescript](https://atom.io/packages/atom-typescript) +* [linter-tslint](https://atom.io/packages/linter-tslint) + +## Contributing + +We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository. + +Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. diff --git a/packages/dev-tools-pages/less/all.less b/packages/dev-tools-pages/less/all.less new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/packages/dev-tools-pages/less/all.less diff --git a/packages/dev-tools-pages/package.json b/packages/dev-tools-pages/package.json new file mode 100644 index 000000000..f73260544 --- /dev/null +++ b/packages/dev-tools-pages/package.json @@ -0,0 +1,58 @@ +{ + "name": "@0xproject/dev-tools-pages", + "version": "0.0.1", + "engines": { + "node": ">=6.12" + }, + "private": true, + "description": "0x Dev tools pages", + "scripts": { + "build": "node --max_old_space_size=8192 ../../node_modules/.bin/webpack --mode production", + "build:ci": "yarn build", + "build:dev": "../../node_modules/.bin/webpack --mode development", + "clean": "shx rm -f public/bundle*", + "lint": "tslint --project . 'ts/**/*.ts' 'ts/**/*.tsx'", + "dev": "webpack-dev-server --mode development --content-base public" + }, + "license": "Apache-2.0", + "dependencies": { + "@0xproject/react-shared": "^1.0.15", + "basscss": "^8.0.3", + "bowser": "^1.9.3", + "less": "^2.7.2", + "lodash": "^4.17.5", + "polished": "^1.9.2", + "react": "^16.4.2", + "react-document-title": "^2.0.3", + "react-dom": "^16.4.2", + "react-helmet": "^5.2.0", + "styled-components": "^3.3.0" + }, + "devDependencies": { + "@types/lodash": "4.14.104", + "@types/node": "*", + "@types/react": "^16.4.2", + "@types/react-dom": "^16.0.7", + "@types/react-helmet": "^5.0.6", + "@types/react-router-dom": "^4.0.4", + "@types/react-tap-event-plugin": "0.0.30", + "@types/styled-components": "^4.0.0", + "awesome-typescript-loader": "^5.2.1", + "copyfiles": "^2.0.0", + "css-loader": "0.23.x", + "less-loader": "^4.1.0", + "make-promises-safe": "^1.1.0", + "raw-loader": "^0.5.1", + "shx": "^0.2.2", + "source-map-loader": "^0.2.4", + "style-loader": "0.23.x", + "terser-webpack-plugin": "^1.1.0", + "tslint": "5.11.0", + "tslint-config-0xproject": "^0.0.2", + "typescript": "3.0.1", + "uglifyjs-webpack-plugin": "^2.0.1", + "webpack": "^4.20.2", + "webpack-cli": "3.1.2", + "webpack-dev-server": "^3.1.9" + } +} diff --git a/packages/dev-tools-pages/public/css/basscss_responsive_custom.css b/packages/dev-tools-pages/public/css/basscss_responsive_custom.css new file mode 100644 index 000000000..5f8bd9117 --- /dev/null +++ b/packages/dev-tools-pages/public/css/basscss_responsive_custom.css @@ -0,0 +1,85 @@ +/* Custom Basscss Responsive Utilities */ + +@media (max-width: 52em) { + .sm-center { + text-align: center; + } + .sm-align-middle { + vertical-align: middle; + } + .sm-align-top { + vertical-align: top; + } + .sm-left-align { + text-align: left; + } + .sm-right-align { + text-align: right; + } + .sm-table-cell { + display: table-cell; + } + .sm-mx-auto { + margin-left: auto; + margin-right: auto; + } + .sm-right { + float: right; + } +} + +@media (min-width: 52em) { + .md-center { + text-align: center; + } + .md-align-middle { + vertical-align: middle; + } + .md-align-top { + vertical-align: top; + } + .md-left-align { + text-align: left; + } + .md-right-align { + text-align: right; + } + .md-table-cell { + display: table-cell; + } + .md-mx-auto { + margin-left: auto; + margin-right: auto; + } + .md-right { + float: right; + } +} + +@media (min-width: 64em) { + .lg-center { + text-align: center; + } + .lg-align-middle { + vertical-align: middle; + } + .lg-align-top { + vertical-align: top; + } + .lg-left-align { + text-align: left; + } + .lg-right-align { + text-align: right; + } + .lg-table-cell { + display: table-cell; + } + .lg-mx-auto { + margin-left: auto; + margin-right: auto; + } + .lg-right { + float: right; + } +} diff --git a/packages/dev-tools-pages/public/css/basscss_responsive_margin.css b/packages/dev-tools-pages/public/css/basscss_responsive_margin.css new file mode 100644 index 000000000..c9f3e855c --- /dev/null +++ b/packages/dev-tools-pages/public/css/basscss_responsive_margin.css @@ -0,0 +1,453 @@ +/* Basscss Responsive Margin */ + +@media (max-width: 52em) { + /* Modified by Fabio Berger to max-width from min-width */ + + .sm-m0 { + margin: 0; + } + .sm-mt0 { + margin-top: 0; + } + .sm-mr0 { + margin-right: 0; + } + .sm-mb0 { + margin-bottom: 0; + } + .sm-ml0 { + margin-left: 0; + } + .sm-mx0 { + margin-left: 0; + margin-right: 0; + } + .sm-my0 { + margin-top: 0; + margin-bottom: 0; + } + + .sm-m1 { + margin: 0.5rem; + } + .sm-mt1 { + margin-top: 0.5rem; + } + .sm-mr1 { + margin-right: 0.5rem; + } + .sm-mb1 { + margin-bottom: 0.5rem; + } + .sm-ml1 { + margin-left: 0.5rem; + } + .sm-mx1 { + margin-left: 0.5rem; + margin-right: 0.5rem; + } + .sm-my1 { + margin-top: 0.5rem; + margin-bottom: 0.5rem; + } + + .sm-m2 { + margin: 1rem; + } + .sm-mt2 { + margin-top: 1rem; + } + .sm-mr2 { + margin-right: 1rem; + } + .sm-mb2 { + margin-bottom: 1rem; + } + .sm-ml2 { + margin-left: 1rem; + } + .sm-mx2 { + margin-left: 1rem; + margin-right: 1rem; + } + .sm-my2 { + margin-top: 1rem; + margin-bottom: 1rem; + } + + .sm-m3 { + margin: 2rem; + } + .sm-mt3 { + margin-top: 2rem; + } + .sm-mr3 { + margin-right: 2rem; + } + .sm-mb3 { + margin-bottom: 2rem; + } + .sm-ml3 { + margin-left: 2rem; + } + .sm-mx3 { + margin-left: 2rem; + margin-right: 2rem; + } + .sm-my3 { + margin-top: 2rem; + margin-bottom: 2rem; + } + + .sm-m4 { + margin: 4rem; + } + .sm-mt4 { + margin-top: 4rem; + } + .sm-mr4 { + margin-right: 4rem; + } + .sm-mb4 { + margin-bottom: 4rem; + } + .sm-ml4 { + margin-left: 4rem; + } + .sm-mx4 { + margin-left: 4rem; + margin-right: 4rem; + } + .sm-my4 { + margin-top: 4rem; + margin-bottom: 4rem; + } + + .sm-mxn1 { + margin-left: -0.5rem; + margin-right: -0.5rem; + } + .sm-mxn2 { + margin-left: -1rem; + margin-right: -1rem; + } + .sm-mxn3 { + margin-left: -2rem; + margin-right: -2rem; + } + .sm-mxn4 { + margin-left: -4rem; + margin-right: -4rem; + } + + .sm-ml-auto { + margin-left: auto; + } + .sm-mr-auto { + margin-right: auto; + } + .sm-mx-auto { + margin-left: auto; + margin-right: auto; + } +} + +@media (min-width: 52em) { + .md-m0 { + margin: 0; + } + .md-mt0 { + margin-top: 0; + } + .md-mr0 { + margin-right: 0; + } + .md-mb0 { + margin-bottom: 0; + } + .md-ml0 { + margin-left: 0; + } + .md-mx0 { + margin-left: 0; + margin-right: 0; + } + .md-my0 { + margin-top: 0; + margin-bottom: 0; + } + + .md-m1 { + margin: 0.5rem; + } + .md-mt1 { + margin-top: 0.5rem; + } + .md-mr1 { + margin-right: 0.5rem; + } + .md-mb1 { + margin-bottom: 0.5rem; + } + .md-ml1 { + margin-left: 0.5rem; + } + .md-mx1 { + margin-left: 0.5rem; + margin-right: 0.5rem; + } + .md-my1 { + margin-top: 0.5rem; + margin-bottom: 0.5rem; + } + + .md-m2 { + margin: 1rem; + } + .md-mt2 { + margin-top: 1rem; + } + .md-mr2 { + margin-right: 1rem; + } + .md-mb2 { + margin-bottom: 1rem; + } + .md-ml2 { + margin-left: 1rem; + } + .md-mx2 { + margin-left: 1rem; + margin-right: 1rem; + } + .md-my2 { + margin-top: 1rem; + margin-bottom: 1rem; + } + + .md-m3 { + margin: 2rem; + } + .md-mt3 { + margin-top: 2rem; + } + .md-mr3 { + margin-right: 2rem; + } + .md-mb3 { + margin-bottom: 2rem; + } + .md-ml3 { + margin-left: 2rem; + } + .md-mx3 { + margin-left: 2rem; + margin-right: 2rem; + } + .md-my3 { + margin-top: 2rem; + margin-bottom: 2rem; + } + + .md-m4 { + margin: 4rem; + } + .md-mt4 { + margin-top: 4rem; + } + .md-mr4 { + margin-right: 4rem; + } + .md-mb4 { + margin-bottom: 4rem; + } + .md-ml4 { + margin-left: 4rem; + } + .md-mx4 { + margin-left: 4rem; + margin-right: 4rem; + } + .md-my4 { + margin-top: 4rem; + margin-bottom: 4rem; + } + + .md-mxn1 { + margin-left: -0.5rem; + margin-right: -0.5rem; + } + .md-mxn2 { + margin-left: -1rem; + margin-right: -1rem; + } + .md-mxn3 { + margin-left: -2rem; + margin-right: -2rem; + } + .md-mxn4 { + margin-left: -4rem; + margin-right: -4rem; + } + + .md-ml-auto { + margin-left: auto; + } + .md-mr-auto { + margin-right: auto; + } + .md-mx-auto { + margin-left: auto; + margin-right: auto; + } +} + +@media (min-width: 64em) { + .lg-m0 { + margin: 0; + } + .lg-mt0 { + margin-top: 0; + } + .lg-mr0 { + margin-right: 0; + } + .lg-mb0 { + margin-bottom: 0; + } + .lg-ml0 { + margin-left: 0; + } + .lg-mx0 { + margin-left: 0; + margin-right: 0; + } + .lg-my0 { + margin-top: 0; + margin-bottom: 0; + } + + .lg-m1 { + margin: 0.5rem; + } + .lg-mt1 { + margin-top: 0.5rem; + } + .lg-mr1 { + margin-right: 0.5rem; + } + .lg-mb1 { + margin-bottom: 0.5rem; + } + .lg-ml1 { + margin-left: 0.5rem; + } + .lg-mx1 { + margin-left: 0.5rem; + margin-right: 0.5rem; + } + .lg-my1 { + margin-top: 0.5rem; + margin-bottom: 0.5rem; + } + + .lg-m2 { + margin: 1rem; + } + .lg-mt2 { + margin-top: 1rem; + } + .lg-mr2 { + margin-right: 1rem; + } + .lg-mb2 { + margin-bottom: 1rem; + } + .lg-ml2 { + margin-left: 1rem; + } + .lg-mx2 { + margin-left: 1rem; + margin-right: 1rem; + } + .lg-my2 { + margin-top: 1rem; + margin-bottom: 1rem; + } + + .lg-m3 { + margin: 2rem; + } + .lg-mt3 { + margin-top: 2rem; + } + .lg-mr3 { + margin-right: 2rem; + } + .lg-mb3 { + margin-bottom: 2rem; + } + .lg-ml3 { + margin-left: 2rem; + } + .lg-mx3 { + margin-left: 2rem; + margin-right: 2rem; + } + .lg-my3 { + margin-top: 2rem; + margin-bottom: 2rem; + } + + .lg-m4 { + margin: 4rem; + } + .lg-mt4 { + margin-top: 4rem; + } + .lg-mr4 { + margin-right: 4rem; + } + .lg-mb4 { + margin-bottom: 4rem; + } + .lg-ml4 { + margin-left: 4rem; + } + .lg-mx4 { + margin-left: 4rem; + margin-right: 4rem; + } + .lg-my4 { + margin-top: 4rem; + margin-bottom: 4rem; + } + + .lg-mxn1 { + margin-left: -0.5rem; + margin-right: -0.5rem; + } + .lg-mxn2 { + margin-left: -1rem; + margin-right: -1rem; + } + .lg-mxn3 { + margin-left: -2rem; + margin-right: -2rem; + } + .lg-mxn4 { + margin-left: -4rem; + margin-right: -4rem; + } + + .lg-ml-auto { + margin-left: auto; + } + .lg-mr-auto { + margin-right: auto; + } + .lg-mx-auto { + margin-left: auto; + margin-right: auto; + } +} diff --git a/packages/dev-tools-pages/public/css/basscss_responsive_padding.css b/packages/dev-tools-pages/public/css/basscss_responsive_padding.css new file mode 100644 index 000000000..e027c2d65 --- /dev/null +++ b/packages/dev-tools-pages/public/css/basscss_responsive_padding.css @@ -0,0 +1,134 @@ +/* Basscss Responsive Padding */ +/* Modified by Fabio Berger to include xs prefix */ + +@media (max-width: 52em) { /* Modified by Fabio Berger to max-width from min-width */ + + .sm-p0 { padding: 0 } + .sm-pt0 { padding-top: 0 } + .sm-pr0 { padding-right: 0 } + .sm-pb0 { padding-bottom: 0 } + .sm-pl0 { padding-left: 0 } + .sm-px0 { padding-left: 0; padding-right: 0 } + .sm-py0 { padding-top: 0; padding-bottom: 0 } + + .sm-p1 { padding: .5rem } + .sm-pt1 { padding-top: .5rem } + .sm-pr1 { padding-right: .5rem } + .sm-pb1 { padding-bottom: .5rem } + .sm-pl1 { padding-left: .5rem } + .sm-px1 { padding-left: .5rem; padding-right: .5rem } + .sm-py1 { padding-top: .5rem; padding-bottom: .5rem } + + .sm-p2 { padding: 1rem } + .sm-pt2 { padding-top: 1rem } + .sm-pr2 { padding-right: 1rem } + .sm-pb2 { padding-bottom: 1rem } + .sm-pl2 { padding-left: 1rem } + .sm-px2 { padding-left: 1rem; padding-right: 1rem } + .sm-py2 { padding-top: 1rem; padding-bottom: 1rem } + + .sm-p3 { padding: 2rem } + .sm-pt3 { padding-top: 2rem } + .sm-pr3 { padding-right: 2rem } + .sm-pb3 { padding-bottom: 2rem } + .sm-pl3 { padding-left: 2rem } + .sm-px3 { padding-left: 2rem; padding-right: 2rem } + .sm-py3 { padding-top: 2rem; padding-bottom: 2rem } + + .sm-p4 { padding: 4rem } + .sm-pt4 { padding-top: 4rem } + .sm-pr4 { padding-right: 4rem } + .sm-pb4 { padding-bottom: 4rem } + .sm-pl4 { padding-left: 4rem } + .sm-px4 { padding-left: 4rem; padding-right: 4rem } + .sm-py4 { padding-top: 4rem; padding-bottom: 4rem } + +} + +@media (min-width: 52em) { + + .md-p0 { padding: 0 } + .md-pt0 { padding-top: 0 } + .md-pr0 { padding-right: 0 } + .md-pb0 { padding-bottom: 0 } + .md-pl0 { padding-left: 0 } + .md-px0 { padding-left: 0; padding-right: 0 } + .md-py0 { padding-top: 0; padding-bottom: 0 } + + .md-p1 { padding: .5rem } + .md-pt1 { padding-top: .5rem } + .md-pr1 { padding-right: .5rem } + .md-pb1 { padding-bottom: .5rem } + .md-pl1 { padding-left: .5rem } + .md-px1 { padding-left: .5rem; padding-right: .5rem } + .md-py1 { padding-top: .5rem; padding-bottom: .5rem } + + .md-p2 { padding: 1rem } + .md-pt2 { padding-top: 1rem } + .md-pr2 { padding-right: 1rem } + .md-pb2 { padding-bottom: 1rem } + .md-pl2 { padding-left: 1rem } + .md-px2 { padding-left: 1rem; padding-right: 1rem } + .md-py2 { padding-top: 1rem; padding-bottom: 1rem } + + .md-p3 { padding: 2rem } + .md-pt3 { padding-top: 2rem } + .md-pr3 { padding-right: 2rem } + .md-pb3 { padding-bottom: 2rem } + .md-pl3 { padding-left: 2rem } + .md-px3 { padding-left: 2rem; padding-right: 2rem } + .md-py3 { padding-top: 2rem; padding-bottom: 2rem } + + .md-p4 { padding: 4rem } + .md-pt4 { padding-top: 4rem } + .md-pr4 { padding-right: 4rem } + .md-pb4 { padding-bottom: 4rem } + .md-pl4 { padding-left: 4rem } + .md-px4 { padding-left: 4rem; padding-right: 4rem } + .md-py4 { padding-top: 4rem; padding-bottom: 4rem } + +} + +@media (min-width: 64em) { + + .lg-p0 { padding: 0 } + .lg-pt0 { padding-top: 0 } + .lg-pr0 { padding-right: 0 } + .lg-pb0 { padding-bottom: 0 } + .lg-pl0 { padding-left: 0 } + .lg-px0 { padding-left: 0; padding-right: 0 } + .lg-py0 { padding-top: 0; padding-bottom: 0 } + + .lg-p1 { padding: .5rem } + .lg-pt1 { padding-top: .5rem } + .lg-pr1 { padding-right: .5rem } + .lg-pb1 { padding-bottom: .5rem } + .lg-pl1 { padding-left: .5rem } + .lg-px1 { padding-left: .5rem; padding-right: .5rem } + .lg-py1 { padding-top: .5rem; padding-bottom: .5rem } + + .lg-p2 { padding: 1rem } + .lg-pt2 { padding-top: 1rem } + .lg-pr2 { padding-right: 1rem } + .lg-pb2 { padding-bottom: 1rem } + .lg-pl2 { padding-left: 1rem } + .lg-px2 { padding-left: 1rem; padding-right: 1rem } + .lg-py2 { padding-top: 1rem; padding-bottom: 1rem } + + .lg-p3 { padding: 2rem } + .lg-pt3 { padding-top: 2rem } + .lg-pr3 { padding-right: 2rem } + .lg-pb3 { padding-bottom: 2rem } + .lg-pl3 { padding-left: 2rem } + .lg-px3 { padding-left: 2rem; padding-right: 2rem } + .lg-py3 { padding-top: 2rem; padding-bottom: 2rem } + + .lg-p4 { padding: 4rem } + .lg-pt4 { padding-top: 4rem } + .lg-pr4 { padding-right: 4rem } + .lg-pb4 { padding-bottom: 4rem } + .lg-pl4 { padding-left: 4rem } + .lg-px4 { padding-left: 4rem; padding-right: 4rem } + .lg-py4 { padding-top: 4rem; padding-bottom: 4rem } + +} diff --git a/packages/dev-tools-pages/public/css/basscss_responsive_type_scale.css b/packages/dev-tools-pages/public/css/basscss_responsive_type_scale.css new file mode 100644 index 000000000..cae23b4e7 --- /dev/null +++ b/packages/dev-tools-pages/public/css/basscss_responsive_type_scale.css @@ -0,0 +1,35 @@ +/* Basscss Responsive Type Scale */ +/* Modified by Fabio Berger to include xs prefix */ + +@media (max-width: 52em) { /* Modified by Fabio Berger to max-width from min-width */ + .sm-h00 { font-size: 4rem } + .sm-h0 { font-size: 3rem } + .sm-h1 { font-size: 2rem } + .sm-h2 { font-size: 1.5rem } + .sm-h3 { font-size: 1.25rem } + .sm-h4 { font-size: 1rem } + .sm-h5 { font-size: .875rem } + .sm-h6 { font-size: .75rem } +} + +@media (min-width: 52em) { + .md-h00 { font-size: 4rem } + .md-h0 { font-size: 3rem } + .md-h1 { font-size: 2rem } + .md-h2 { font-size: 1.5rem } + .md-h3 { font-size: 1.25rem } + .md-h4 { font-size: 1rem } + .md-h5 { font-size: .875rem } + .md-h6 { font-size: .75rem } +} + +@media (min-width: 64em) { + .lg-h00 { font-size: 4rem } + .lg-h0 { font-size: 3rem } + .lg-h1 { font-size: 2rem } + .lg-h2 { font-size: 1.5rem } + .lg-h3 { font-size: 1.25rem } + .lg-h4 { font-size: 1rem } + .lg-h5 { font-size: .875rem } + .lg-h6 { font-size: .75rem } +} diff --git a/packages/dev-tools-pages/public/images/favicon/favicon-2-16x16.png b/packages/dev-tools-pages/public/images/favicon/favicon-2-16x16.png Binary files differnew file mode 100755 index 000000000..68c493c4f --- /dev/null +++ b/packages/dev-tools-pages/public/images/favicon/favicon-2-16x16.png diff --git a/packages/dev-tools-pages/public/images/favicon/favicon-2-32x32.png b/packages/dev-tools-pages/public/images/favicon/favicon-2-32x32.png Binary files differnew file mode 100755 index 000000000..a5abb0eb3 --- /dev/null +++ b/packages/dev-tools-pages/public/images/favicon/favicon-2-32x32.png diff --git a/packages/dev-tools-pages/public/images/favicon/favicon.ico b/packages/dev-tools-pages/public/images/favicon/favicon.ico Binary files differnew file mode 100755 index 000000000..b7ada2a1c --- /dev/null +++ b/packages/dev-tools-pages/public/images/favicon/favicon.ico diff --git a/packages/dev-tools-pages/public/index.html b/packages/dev-tools-pages/public/index.html new file mode 100644 index 000000000..f62d7b255 --- /dev/null +++ b/packages/dev-tools-pages/public/index.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<html> + +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="description" content="" /> + <meta property="og:type" content="website" /> + <meta property="og:title" content="0x" /> + <meta property="og:description" content="" /> + <meta property="og:image" content="/images/og_image.png" /> + <title>0x: The Protocol for Trading Tokens</title> + <link rel="icon" type="image/png" href="/images/favicon/favicon-2-32x32.png" sizes="32x32" /> + <link rel="icon" type="image/png" href="/images/favicon/favicon-2-16x16.png" sizes="16x16" /> + <link rel="stylesheet" href="/css/basscss_responsive_custom.css"> + <link rel="stylesheet" href="/css/basscss_responsive_padding.css"> + <link rel="stylesheet" href="/css/basscss_responsive_margin.css"> + <link rel="stylesheet" href="/css/basscss_responsive_type_scale.css"> +</head> + +<body style="margin: 0px; min-width: 355px;"> + <div id="app"></div> + <script type="text/javascript" crossorigin="anonymous" src="/bundle.js" charset="utf-8"></script> +</body> + +</html>
\ No newline at end of file diff --git a/packages/dev-tools-pages/ts/components/meta_tags.tsx b/packages/dev-tools-pages/ts/components/meta_tags.tsx new file mode 100644 index 000000000..f6c43d23f --- /dev/null +++ b/packages/dev-tools-pages/ts/components/meta_tags.tsx @@ -0,0 +1,25 @@ +import * as React from 'react'; +import { Helmet } from 'react-helmet'; + +export interface MetaTagsProps { + title: string; + description: string; + imgSrc?: string; +} + +export const MetaTags: React.StatelessComponent<MetaTagsProps> = ({ title, description, imgSrc }) => ( + <Helmet> + <title>{title}</title> + <meta name="description" content={description} /> + <meta property="og:title" content={title} /> + <meta property="og:description" content={description} /> + <meta property="og:type" content="website" /> + <meta property="og:image" content={imgSrc} /> + <meta name="twitter:site" content="@0xproject" /> + <meta name="twitter:image" content={imgSrc} /> + </Helmet> +); + +MetaTags.defaultProps = { + imgSrc: '/images/og_image.png', +}; diff --git a/packages/dev-tools-pages/ts/components/ui/button.tsx b/packages/dev-tools-pages/ts/components/ui/button.tsx new file mode 100644 index 000000000..754eca40e --- /dev/null +++ b/packages/dev-tools-pages/ts/components/ui/button.tsx @@ -0,0 +1,59 @@ +import { darken, saturate } from 'polished'; +import * as React from 'react'; +import styled from 'styled-components'; + +/** + * AN EXAMPLE OF HOW TO CREATE A STYLED COMPONENT USING STYLED-COMPONENTS + * SEE: https://www.styled-components.com/docs/basics#coming-from-css + */ +export interface ButtonProps { + backgroundColor?: string; + borderColor?: string; + width?: string; + padding?: string; + type?: string; + isDisabled?: boolean; + onClick?: (event: React.MouseEvent<HTMLElement>) => void; + className?: string; +} + +const PlainButton: React.StatelessComponent<ButtonProps> = ({ children, isDisabled, onClick, type, className }) => ( + <button type={type} className={className} onClick={isDisabled ? undefined : onClick} disabled={isDisabled}> + {children} + </button> +); + +const darkenOnHoverAmount = 0.1; +const darkenOnActiveAmount = 0.2; +const saturateOnFocusAmount = 0.2; +export const Button = styled(PlainButton)` + cursor: ${props => (props.isDisabled ? 'default' : 'pointer')}; + transition: background-color, opacity 0.5s ease; + padding: ${props => props.padding}; + border-radius: 3px; + outline: none; + width: ${props => props.width}; + background-color: ${props => (props.backgroundColor ? props.backgroundColor : 'none')}; + border: ${props => (props.borderColor ? `1px solid ${props.backgroundColor}` : 'none')}; + &:hover { + background-color: ${props => + !props.isDisabled ? darken(darkenOnHoverAmount, props.backgroundColor) : ''} !important; + } + &:active { + background-color: ${props => (!props.isDisabled ? darken(darkenOnActiveAmount, props.backgroundColor) : '')}; + } + &:disabled { + opacity: 0.5; + } + &:focus { + background-color: ${props => saturate(saturateOnFocusAmount, props.backgroundColor)}; + } +`; + +Button.defaultProps = { + backgroundColor: 'red', + width: 'auto', + isDisabled: false, + padding: '1em 2.2em', +}; +Button.displayName = 'Button'; diff --git a/packages/dev-tools-pages/ts/components/ui/container.tsx b/packages/dev-tools-pages/ts/components/ui/container.tsx new file mode 100644 index 000000000..f2ae68b70 --- /dev/null +++ b/packages/dev-tools-pages/ts/components/ui/container.tsx @@ -0,0 +1,55 @@ +import * as React from 'react'; + +type StringOrNum = string | number; + +export type ContainerTag = 'div' | 'span'; + +export interface ContainerProps { + marginTop?: StringOrNum; + marginBottom?: StringOrNum; + marginRight?: StringOrNum; + marginLeft?: StringOrNum; + padding?: StringOrNum; + paddingTop?: StringOrNum; + paddingBottom?: StringOrNum; + paddingRight?: StringOrNum; + paddingLeft?: StringOrNum; + backgroundColor?: string; + borderRadius?: StringOrNum; + maxWidth?: StringOrNum; + maxHeight?: StringOrNum; + width?: StringOrNum; + height?: StringOrNum; + minWidth?: StringOrNum; + minHeight?: StringOrNum; + isHidden?: boolean; + className?: string; + position?: 'absolute' | 'fixed' | 'relative' | 'unset'; + display?: 'inline-block' | 'block' | 'inline-flex' | 'inline'; + top?: string; + left?: string; + right?: string; + bottom?: string; + zIndex?: number; + Tag?: ContainerTag; + cursor?: string; + id?: string; + onClick?: (event: React.MouseEvent<HTMLElement>) => void; + overflowX?: 'scroll' | 'hidden' | 'auto' | 'visible'; +} + +export const Container: React.StatelessComponent<ContainerProps> = props => { + const { children, className, Tag, isHidden, id, onClick, ...style } = props; + const visibility = isHidden ? 'hidden' : undefined; + return ( + <Tag id={id} style={{ ...style, visibility }} className={className} onClick={onClick}> + {children} + </Tag> + ); +}; + +Container.defaultProps = { + Tag: 'div', +}; + +Container.displayName = 'Container'; diff --git a/packages/dev-tools-pages/ts/components/ui/text.tsx b/packages/dev-tools-pages/ts/components/ui/text.tsx new file mode 100644 index 000000000..8e314beae --- /dev/null +++ b/packages/dev-tools-pages/ts/components/ui/text.tsx @@ -0,0 +1,74 @@ +import { colors } from '@0xproject/react-shared'; +import { darken } from 'polished'; +import * as React from 'react'; +import styled from 'styled-components'; + +export type TextTag = 'p' | 'div' | 'span' | 'label' | 'h1' | 'h2' | 'h3' | 'h4' | 'i'; + +export interface TextProps { + className?: string; + Tag?: TextTag; + fontSize?: string; + fontFamily?: string; + fontStyle?: string; + fontColor?: string; + lineHeight?: string; + minHeight?: string; + center?: boolean; + fontWeight?: number | string; + textDecorationLine?: string; + onClick?: (event: React.MouseEvent<HTMLElement>) => void; + hoverColor?: string; + noWrap?: boolean; + display?: string; +} + +const PlainText: React.StatelessComponent<TextProps> = ({ children, className, onClick, Tag }) => ( + <Tag className={className} onClick={onClick}> + {children} + </Tag> +); + +export const Text = styled(PlainText)` + font-family: ${props => props.fontFamily}; + font-style: ${props => props.fontStyle}; + font-weight: ${props => props.fontWeight}; + font-size: ${props => props.fontSize}; + text-decoration-line: ${props => props.textDecorationLine}; + ${props => (props.lineHeight ? `line-height: ${props.lineHeight}` : '')}; + ${props => (props.center ? 'text-align: center' : '')}; + color: ${props => props.fontColor}; + ${props => (props.minHeight ? `min-height: ${props.minHeight}` : '')}; + ${props => (props.onClick ? 'cursor: pointer' : '')}; + transition: color 0.5s ease; + ${props => (props.noWrap ? 'white-space: nowrap' : '')}; + ${props => (props.display ? `display: ${props.display}` : '')}; + &:hover { + ${props => (props.onClick ? `color: ${props.hoverColor || darken(0.3, props.fontColor || 'black')}` : '')}; + } +`; + +Text.defaultProps = { + fontFamily: 'Roboto', + fontStyle: 'normal', + fontWeight: 400, + fontColor: colors.black, + fontSize: '15px', + lineHeight: '1.5em', + textDecorationLine: 'none', + Tag: 'div', + noWrap: false, +}; + +Text.displayName = 'Text'; + +export const Title: React.StatelessComponent<TextProps> = props => <Text {...props} />; + +Title.defaultProps = { + Tag: 'h2', + fontSize: '20px', + fontWeight: 600, + fontColor: colors.black, +}; + +Title.displayName = 'Title'; diff --git a/packages/dev-tools-pages/ts/globals.d.ts b/packages/dev-tools-pages/ts/globals.d.ts new file mode 100644 index 000000000..d0890161c --- /dev/null +++ b/packages/dev-tools-pages/ts/globals.d.ts @@ -0,0 +1,9 @@ +declare module 'whatwg-fetch'; +declare module 'react-document-title'; + +declare module '*.json' { + const json: any; + /* tslint:disable */ + export default json; + /* tslint:enable */ +} diff --git a/packages/dev-tools-pages/ts/index.tsx b/packages/dev-tools-pages/ts/index.tsx new file mode 100644 index 000000000..4591c6d76 --- /dev/null +++ b/packages/dev-tools-pages/ts/index.tsx @@ -0,0 +1,17 @@ +import * as React from 'react'; +import { render } from 'react-dom'; +import { MetaTags } from 'ts/components/meta_tags'; +import { Landing } from 'ts/pages/landing'; + +import 'basscss/css/basscss.css'; + +const DOCUMENT_TITLE = ''; +const DOCUMENT_DESCRIPTION = ''; + +render( + <div> + <MetaTags title={DOCUMENT_TITLE} description={DOCUMENT_DESCRIPTION} /> + <Landing /> + </div>, + document.getElementById('app'), +); diff --git a/packages/dev-tools-pages/ts/pages/landing.tsx b/packages/dev-tools-pages/ts/pages/landing.tsx new file mode 100644 index 000000000..a70a9de46 --- /dev/null +++ b/packages/dev-tools-pages/ts/pages/landing.tsx @@ -0,0 +1,27 @@ +import * as _ from 'lodash'; +import * as React from 'react'; + +import { Button } from '../components/ui/button'; +import { Container } from '../components/ui/container'; +import { Text } from '../components/ui/text'; + +interface LandingProps {} + +interface LandingState {} + +export class Landing extends React.Component<LandingProps, LandingState> { + constructor(props: LandingProps) { + super(props); + } + public render(): React.ReactNode { + return ( + <Container id="landing" className="clearfix"> + <Container className="mx-auto p4" width="200px"> + <Button> + <Text fontColor="white">Click me!</Text> + </Button> + </Container> + </Container> + ); + } +} diff --git a/packages/dev-tools-pages/ts/utils/utils.ts b/packages/dev-tools-pages/ts/utils/utils.ts new file mode 100644 index 000000000..b274706a2 --- /dev/null +++ b/packages/dev-tools-pages/ts/utils/utils.ts @@ -0,0 +1,32 @@ +import * as bowser from 'bowser'; +import * as _ from 'lodash'; + +export const utils = { + getColSize(items: number): number { + const bassCssGridSize = 12; // Source: http://basscss.com/#basscss-grid + const colSize = bassCssGridSize / items; + if (!_.isInteger(colSize)) { + throw new Error(`Number of cols must be divisible by ${bassCssGridSize}`); + } + return colSize; + }, + getCurrentBaseUrl(): string { + const port = window.location.port; + const hasPort = !_.isUndefined(port); + const baseUrl = `https://${window.location.hostname}${hasPort ? `:${port}` : ''}`; + return baseUrl; + }, + onPageLoadPromise: new Promise<void>((resolve, _reject) => { + if (document.readyState === 'complete') { + resolve(); + return; + } + window.onload = () => resolve(); + }), + openUrl(url: string): void { + window.open(url, '_blank'); + }, + isMobileOperatingSystem(): boolean { + return bowser.mobile; + }, +}; diff --git a/packages/dev-tools-pages/tsconfig.json b/packages/dev-tools-pages/tsconfig.json new file mode 100644 index 000000000..6421cd459 --- /dev/null +++ b/packages/dev-tools-pages/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../tsconfig", + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "outDir": "./transpiled/", + "jsx": "react", + "baseUrl": "./", + "allowJs": true, + "strictNullChecks": false, + "noImplicitThis": false, + // tsconfig.json at the monorepo root contains some options required for + // project references which do not work for website. We override those + // options here. + "declaration": false, + "declarationMap": false, + "composite": false, + "paths": { + "*": ["node_modules/@types/*", "*"] + } + }, + "include": ["./ts/**/*"] +} diff --git a/packages/dev-tools-pages/tslint.json b/packages/dev-tools-pages/tslint.json new file mode 100644 index 000000000..b55ffe90f --- /dev/null +++ b/packages/dev-tools-pages/tslint.json @@ -0,0 +1,10 @@ +{ + "extends": ["@0xproject/tslint-config"], + "rules": { + "no-implicit-dependencies": false, + "no-object-literal-type-assertion": false, + "completed-docs": false, + "prefer-function-over-method": false, + "custom-no-magic-numbers": false + } +} diff --git a/packages/dev-tools-pages/webpack.config.js b/packages/dev-tools-pages/webpack.config.js new file mode 100644 index 000000000..6dfcf74e7 --- /dev/null +++ b/packages/dev-tools-pages/webpack.config.js @@ -0,0 +1,86 @@ +const path = require('path'); +const webpack = require('webpack'); +const TerserPlugin = require('terser-webpack-plugin'); +const childProcess = require('child_process'); + +const config = { + entry: ['./ts/index.tsx'], + output: { + path: path.join(__dirname, '/public'), + filename: 'bundle.js', + chunkFilename: 'bundle-[name].js', + publicPath: '/', + }, + devtool: 'source-map', + resolve: { + modules: [path.join(__dirname, '/ts'), 'node_modules'], + extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'], + alias: { + ts: path.join(__dirname, '/ts'), + less: path.join(__dirname, '/less'), + }, + }, + module: { + rules: [ + { + test: /\.js$/, + loader: 'source-map-loader', + exclude: [ + // instead of /\/node_modules\// + path.join(process.cwd(), 'node_modules'), + path.join(process.cwd(), '../..', 'node_modules'), + ], + }, + { + test: /\.tsx?$/, + loader: 'awesome-typescript-loader', + }, + { + test: /\.md$/, + use: 'raw-loader', + }, + { + test: /\.less$/, + loader: 'style-loader!css-loader!less-loader', + exclude: /node_modules/, + }, + { + test: /\.css$/, + loaders: ['style-loader', 'css-loader'], + }, + ], + }, + optimization: { + minimizer: [ + new TerserPlugin({ + sourceMap: true, + }), + ], + }, + devServer: { + port: 3572, + disableHostCheck: true, + }, +}; + +module.exports = (_env, argv) => { + let plugins = []; + if (argv.mode === 'development') { + config.mode = 'development'; + } else { + config.mode = 'production'; + plugins = plugins.concat([ + new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: JSON.stringify(process.env.NODE_ENV), + }, + }), + ]); + } + console.log('i ï½¢atlï½£: Mode: ', config.mode); + + config.plugins = plugins; + console.log('i ï½¢atlï½£: Plugin Count: ', config.plugins.length); + + return config; +}; diff --git a/packages/ethereum-types/CHANGELOG.json b/packages/ethereum-types/CHANGELOG.json index e955f4d04..60fb8c806 100644 --- a/packages/ethereum-types/CHANGELOG.json +++ b/packages/ethereum-types/CHANGELOG.json @@ -1,5 +1,14 @@ [ { + "version": "1.1.0", + "changes": [ + { + "note": "Add `JSONRPCResponseError` and error field on `JSONRPCResponsePayload`.", + "pr": 1102 + } + ] + }, + { "timestamp": 1538693146, "version": "1.0.11", "changes": [ diff --git a/packages/ethereum-types/src/index.ts b/packages/ethereum-types/src/index.ts index 7e8b9de3e..ddd472010 100644 --- a/packages/ethereum-types/src/index.ts +++ b/packages/ethereum-types/src/index.ts @@ -113,10 +113,16 @@ export interface JSONRPCRequestPayload { jsonrpc: string; } +export interface JSONRPCResponseError { + message: string; + code: number; +} + export interface JSONRPCResponsePayload { result: any; id: number; jsonrpc: string; + error?: JSONRPCResponseError; } export interface AbstractBlock { diff --git a/packages/fill-scenarios/package.json b/packages/fill-scenarios/package.json index 0616454d6..dd6ede3e0 100644 --- a/packages/fill-scenarios/package.json +++ b/packages/fill-scenarios/package.json @@ -45,7 +45,7 @@ "@0xproject/utils": "^2.0.2", "@0xproject/web3-wrapper": "^3.0.3", "ethereum-types": "^1.0.11", - "ethers": "4.0.0-beta.14", + "ethers": "~4.0.4", "lodash": "^4.17.5" }, "publishConfig": { diff --git a/packages/instant/package.json b/packages/instant/package.json index 26c370e4c..ff40ba2ea 100644 --- a/packages/instant/package.json +++ b/packages/instant/package.json @@ -43,15 +43,19 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/instant/README.md", "dependencies": { - "@0xproject/connect": "^2.0.4", + "@0xproject/asset-buyer": "^2.0.0", "@0xproject/types": "^1.1.4", "@0xproject/typescript-typings": "^2.0.2", - "@0xproject/utils": "^1.0.11", + "@0xproject/utils": "^2.0.2", "@0xproject/web3-wrapper": "^3.0.3", "ethereum-types": "^1.0.11", "lodash": "^4.17.10", + "polished": "^2.2.0", "react": "^16.5.2", - "react-dom": "^16.5.2" + "react-dom": "^16.5.2", + "react-redux": "^5.0.7", + "redux": "^4.0.0", + "styled-components": "^3.4.9" }, "devDependencies": { "@0xproject/tslint-config": "^1.0.8", @@ -59,8 +63,10 @@ "@types/enzyme-adapter-react-16": "^1.0.3", "@types/lodash": "^4.14.116", "@types/node": "*", - "@types/react": "16.4.7", + "@types/react": "^16.4.16", "@types/react-dom": "^16.0.8", + "@types/react-redux": "^6.0.9", + "@types/redux": "^3.6.0", "awesome-typescript-loader": "^5.2.1", "copyfiles": "^1.2.0", "enzyme": "^3.6.0", diff --git a/packages/instant/public/index.html b/packages/instant/public/index.html index 45968a3c9..fb041745e 100644 --- a/packages/instant/public/index.html +++ b/packages/instant/public/index.html @@ -6,6 +6,19 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>0x Instant Dev Environment</title> <script type="text/javascript" src="/main.bundle.js" charset="utf-8"></script> + <style> + #zeroExInstantContainer { + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + } + + body { + margin: 0; + background-color: rgba(0, 0, 0, 0.2); + } + </style> </head> <body> diff --git a/packages/instant/src/components/amount_input.tsx b/packages/instant/src/components/amount_input.tsx new file mode 100644 index 000000000..38810063d --- /dev/null +++ b/packages/instant/src/components/amount_input.tsx @@ -0,0 +1,47 @@ +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; +import * as React from 'react'; + +import { ColorOption } from '../style/theme'; + +import { Container, Input } from './ui'; + +export interface AmountInputProps { + fontColor?: ColorOption; + fontSize?: string; + value?: BigNumber; + onChange?: (value?: BigNumber) => void; +} + +export class AmountInput extends React.Component<AmountInputProps> { + public render(): React.ReactNode { + const { fontColor, fontSize, value } = this.props; + return ( + <Container borderBottom="1px solid rgba(255,255,255,0.3)" display="inline-block"> + <Input + fontColor={fontColor} + fontSize={fontSize} + onChange={this._handleChange} + value={!_.isUndefined(value) ? value.toString() : ''} + placeholder="0.00" + width="2em" + /> + </Container> + ); + } + private readonly _handleChange = (event: React.ChangeEvent<HTMLInputElement>): void => { + const value = event.target.value; + let bigNumberValue; + if (!_.isEmpty(value)) { + try { + bigNumberValue = new BigNumber(event.target.value); + } catch { + // We don't want to allow values that can't be a BigNumber, so don't even call onChange. + return; + } + } + if (!_.isUndefined(this.props.onChange)) { + this.props.onChange(bigNumberValue); + } + }; +} diff --git a/packages/instant/src/components/buy_button.tsx b/packages/instant/src/components/buy_button.tsx new file mode 100644 index 000000000..5a32b9575 --- /dev/null +++ b/packages/instant/src/components/buy_button.tsx @@ -0,0 +1,19 @@ +import * as React from 'react'; + +import { ColorOption } from '../style/theme'; + +import { Button, Container, Text } from './ui'; + +export interface BuyButtonProps {} + +export const BuyButton: React.StatelessComponent<BuyButtonProps> = props => ( + <Container padding="20px" width="100%"> + <Button width="100%"> + <Text fontColor={ColorOption.white} fontWeight={600} fontSize="20px"> + Buy + </Text> + </Button> + </Container> +); + +BuyButton.displayName = 'BuyButton'; diff --git a/packages/instant/src/components/instant_heading.tsx b/packages/instant/src/components/instant_heading.tsx new file mode 100644 index 000000000..be0414b8d --- /dev/null +++ b/packages/instant/src/components/instant_heading.tsx @@ -0,0 +1,45 @@ +import * as React from 'react'; + +import { SelectedAssetAmountInput } from '../containers/selected_asset_amount_input'; +import { ColorOption } from '../style/theme'; + +import { Container, Flex, Text } from './ui'; + +export interface InstantHeadingProps {} + +export const InstantHeading: React.StatelessComponent<InstantHeadingProps> = props => ( + <Container backgroundColor={ColorOption.primaryColor} padding="20px" width="100%" borderRadius="3px 3px 0px 0px"> + <Container marginBottom="5px"> + <Text + letterSpacing="1px" + fontColor={ColorOption.white} + opacity={0.7} + fontWeight={500} + textTransform="uppercase" + fontSize="12px" + > + I want to buy + </Text> + </Container> + <Flex direction="row" justify="space-between"> + <Container> + <SelectedAssetAmountInput fontSize="45px" /> + <Container display="inline-block" marginLeft="10px"> + <Text fontSize="45px" fontColor={ColorOption.white} textTransform="uppercase"> + rep + </Text> + </Container> + </Container> + <Flex direction="column" justify="space-between"> + <Container marginBottom="5px"> + <Text fontSize="16px" fontColor={ColorOption.white} fontWeight={500}> + 0 ETH + </Text> + </Container> + <Text fontSize="16px" fontColor={ColorOption.white} opacity={0.7}> + $0.00 + </Text> + </Flex> + </Flex> + </Container> +); diff --git a/packages/instant/src/components/order_details.tsx b/packages/instant/src/components/order_details.tsx new file mode 100644 index 000000000..dbf2c1f0b --- /dev/null +++ b/packages/instant/src/components/order_details.tsx @@ -0,0 +1,62 @@ +import * as React from 'react'; + +import { ColorOption } from '../style/theme'; + +import { Container, Flex, Text } from './ui'; + +export interface OrderDetailsProps {} + +export const OrderDetails: React.StatelessComponent<OrderDetailsProps> = props => ( + <Container padding="20px" width="100%"> + <Container marginBottom="10px"> + <Text + letterSpacing="1px" + fontColor={ColorOption.primaryColor} + fontWeight={600} + textTransform="uppercase" + fontSize="14px" + > + Order Details + </Text> + </Container> + <OrderDetailsRow name="Token Price" primaryValue=".013 ETH" secondaryValue="$24.32" /> + <OrderDetailsRow name="Fee" primaryValue=".005 ETH" secondaryValue="$1.04" /> + <OrderDetailsRow name="Total Cost" primaryValue="1.66 ETH" secondaryValue="$589.56" shouldEmphasize={true} /> + </Container> +); + +OrderDetails.displayName = 'OrderDetails'; + +export interface OrderDetailsRowProps { + name: string; + primaryValue: string; + secondaryValue: string; + shouldEmphasize?: boolean; +} + +export const OrderDetailsRow: React.StatelessComponent<OrderDetailsRowProps> = props => { + const fontWeight = props.shouldEmphasize ? 700 : 400; + return ( + <Container padding="10px 0px" borderTop="1px dashed" borderColor={ColorOption.feintGrey}> + <Flex justify="space-between"> + <Text fontWeight={fontWeight} fontColor={ColorOption.grey}> + {props.name} + </Text> + <Container> + <Container marginRight="3px" display="inline-block"> + <Text fontColor={ColorOption.lightGrey}>({props.secondaryValue}) </Text> + </Container> + <Text fontWeight={fontWeight} fontColor={ColorOption.grey}> + {props.primaryValue} + </Text> + </Container> + </Flex> + </Container> + ); +}; + +OrderDetailsRow.defaultProps = { + shouldEmphasize: false, +}; + +OrderDetailsRow.displayName = 'OrderDetailsRow'; diff --git a/packages/instant/src/components/ui/button.tsx b/packages/instant/src/components/ui/button.tsx new file mode 100644 index 000000000..1fcb2591c --- /dev/null +++ b/packages/instant/src/components/ui/button.tsx @@ -0,0 +1,60 @@ +import { darken, saturate } from 'polished'; +import * as React from 'react'; + +import { ColorOption, styled } from '../../style/theme'; + +export interface ButtonProps { + backgroundColor?: ColorOption; + borderColor?: ColorOption; + width?: string; + padding?: string; + type?: string; + isDisabled?: boolean; + onClick?: (event: React.MouseEvent<HTMLElement>) => void; + className?: string; +} + +const PlainButton: React.StatelessComponent<ButtonProps> = ({ children, isDisabled, onClick, type, className }) => ( + <button type={type} className={className} onClick={isDisabled ? undefined : onClick} disabled={isDisabled}> + {children} + </button> +); + +const darkenOnHoverAmount = 0.1; +const darkenOnActiveAmount = 0.2; +const saturateOnFocusAmount = 0.2; +export const Button = styled(PlainButton)` + cursor: ${props => (props.isDisabled ? 'default' : 'pointer')}; + transition: background-color, opacity 0.5s ease; + padding: ${props => props.padding}; + border-radius: 3px; + outline: none; + width: ${props => props.width}; + background-color: ${props => (props.backgroundColor ? props.theme[props.backgroundColor] : 'none')}; + border: ${props => (props.borderColor ? `1px solid ${props.theme[props.borderColor]}` : 'none')}; + &:hover { + background-color: ${props => + !props.isDisabled + ? darken(darkenOnHoverAmount, props.theme[props.backgroundColor || 'white']) + : ''} !important; + } + &:active { + background-color: ${props => + !props.isDisabled ? darken(darkenOnActiveAmount, props.theme[props.backgroundColor || 'white']) : ''}; + } + &:disabled { + opacity: 0.5; + } + &:focus { + background-color: ${props => saturate(saturateOnFocusAmount, props.theme[props.backgroundColor || 'white'])}; + } +`; + +Button.defaultProps = { + backgroundColor: ColorOption.primaryColor, + width: 'auto', + isDisabled: false, + padding: '1em 2.2em', +}; + +Button.displayName = 'Button'; diff --git a/packages/instant/src/components/ui/container.tsx b/packages/instant/src/components/ui/container.tsx new file mode 100644 index 000000000..c45f6e5e9 --- /dev/null +++ b/packages/instant/src/components/ui/container.tsx @@ -0,0 +1,64 @@ +import * as React from 'react'; + +import { ColorOption, styled } from '../../style/theme'; +import { cssRuleIfExists } from '../../style/util'; + +export interface ContainerProps { + display?: string; + position?: string; + top?: string; + right?: string; + bottom?: string; + left?: string; + width?: string; + maxWidth?: string; + margin?: string; + marginTop?: string; + marginRight?: string; + marginBottom?: string; + marginLeft?: string; + padding?: string; + borderRadius?: string; + border?: string; + borderColor?: ColorOption; + borderTop?: string; + borderBottom?: string; + className?: string; + backgroundColor?: ColorOption; + hasBoxShadow?: boolean; +} + +const PlainContainer: React.StatelessComponent<ContainerProps> = ({ children, className }) => ( + <div className={className}>{children}</div> +); + +export const Container = styled(PlainContainer)` + box-sizing: border-box; + ${props => cssRuleIfExists(props, 'display')} + ${props => cssRuleIfExists(props, 'position')} + ${props => cssRuleIfExists(props, 'top')} + ${props => cssRuleIfExists(props, 'right')} + ${props => cssRuleIfExists(props, 'bottom')} + ${props => cssRuleIfExists(props, 'left')} + ${props => cssRuleIfExists(props, 'width')} + ${props => cssRuleIfExists(props, 'max-width')} + ${props => cssRuleIfExists(props, 'margin')} + ${props => cssRuleIfExists(props, 'margin-top')} + ${props => cssRuleIfExists(props, 'margin-right')} + ${props => cssRuleIfExists(props, 'margin-bottom')} + ${props => cssRuleIfExists(props, 'margin-left')} + ${props => cssRuleIfExists(props, 'padding')} + ${props => cssRuleIfExists(props, 'border-radius')} + ${props => cssRuleIfExists(props, 'border')} + ${props => cssRuleIfExists(props, 'border-top')} + ${props => cssRuleIfExists(props, 'border-bottom')} + ${props => (props.hasBoxShadow ? `box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1)` : '')}; + background-color: ${props => (props.backgroundColor ? props.theme[props.backgroundColor] : 'none')}; + border-color: ${props => (props.borderColor ? props.theme[props.borderColor] : 'none')}; +`; + +Container.defaultProps = { + display: 'block', +}; + +Container.displayName = 'Container'; diff --git a/packages/instant/src/components/ui/flex.tsx b/packages/instant/src/components/ui/flex.tsx new file mode 100644 index 000000000..327e91926 --- /dev/null +++ b/packages/instant/src/components/ui/flex.tsx @@ -0,0 +1,37 @@ +import * as React from 'react'; + +import { ColorOption, styled } from '../../style/theme'; +import { cssRuleIfExists } from '../../style/util'; + +export interface FlexProps { + direction?: 'row' | 'column'; + flexWrap?: 'wrap' | 'nowrap'; + justify?: 'flex-start' | 'center' | 'space-around' | 'space-between' | 'space-evenly' | 'flex-end'; + align?: 'flex-start' | 'center' | 'space-around' | 'space-between' | 'space-evenly' | 'flex-end'; + width?: string; + backgroundColor?: ColorOption; + className?: string; +} + +const PlainFlex: React.StatelessComponent<FlexProps> = ({ children, className }) => ( + <div className={className}>{children}</div> +); + +export const Flex = styled(PlainFlex)` + display: flex; + flex-direction: ${props => props.direction}; + flex-wrap: ${props => props.flexWrap}; + justify-content: ${props => props.justify}; + align-items: ${props => props.align}; + ${props => cssRuleIfExists(props, 'width')} + background-color: ${props => (props.backgroundColor ? props.theme[props.backgroundColor] : 'none')}; +`; + +Flex.defaultProps = { + direction: 'row', + flexWrap: 'nowrap', + justify: 'center', + align: 'center', +}; + +Flex.displayName = 'Flex'; diff --git a/packages/instant/src/components/ui/index.ts b/packages/instant/src/components/ui/index.ts new file mode 100644 index 000000000..bf5f6c700 --- /dev/null +++ b/packages/instant/src/components/ui/index.ts @@ -0,0 +1,5 @@ +export { Text, Title } from './text'; +export { Button } from './button'; +export { Flex } from './flex'; +export { Container } from './container'; +export { Input } from './input'; diff --git a/packages/instant/src/components/ui/input.tsx b/packages/instant/src/components/ui/input.tsx new file mode 100644 index 000000000..f8c6b6ef6 --- /dev/null +++ b/packages/instant/src/components/ui/input.tsx @@ -0,0 +1,40 @@ +import * as React from 'react'; + +import { ColorOption, styled } from '../../style/theme'; + +export interface InputProps { + className?: string; + value?: string; + width?: string; + fontSize?: string; + fontColor?: ColorOption; + placeholder?: string; + onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void; +} + +const PlainInput: React.StatelessComponent<InputProps> = ({ value, className, placeholder, onChange }) => ( + <input className={className} value={value} onChange={onChange} placeholder={placeholder} /> +); + +export const Input = styled(PlainInput)` + font-size: ${props => props.fontSize}; + width: ${props => props.width}; + padding: 0.1em 0em; + font-family: 'Inter UI'; + color: ${props => props.theme[props.fontColor || 'white']}; + background: transparent; + outline: none; + border: none; + &::placeholder { + color: ${props => props.theme[props.fontColor || 'white']}; + opacity: 0.5; + } +`; + +Input.defaultProps = { + width: 'auto', + fontColor: ColorOption.white, + fontSize: '12px', +}; + +Input.displayName = 'Input'; diff --git a/packages/instant/src/components/ui/text.tsx b/packages/instant/src/components/ui/text.tsx new file mode 100644 index 000000000..9fb8ea26f --- /dev/null +++ b/packages/instant/src/components/ui/text.tsx @@ -0,0 +1,80 @@ +import { darken } from 'polished'; +import * as React from 'react'; + +import { ColorOption, styled } from '../../style/theme'; + +export interface TextProps { + fontColor?: ColorOption; + fontFamily?: string; + fontStyle?: string; + fontSize?: string; + opacity?: number; + letterSpacing?: string; + textTransform?: string; + lineHeight?: string; + className?: string; + minHeight?: string; + center?: boolean; + fontWeight?: number | string; + textDecorationLine?: string; + onClick?: (event: React.MouseEvent<HTMLElement>) => void; + hoverColor?: string; + noWrap?: boolean; + display?: string; +} + +const PlainText: React.StatelessComponent<TextProps> = ({ children, className, onClick }) => ( + <div className={className} onClick={onClick}> + {children} + </div> +); + +const darkenOnHoverAmount = 0.3; +export const Text = styled(PlainText)` + font-family: ${props => props.fontFamily}; + font-style: ${props => props.fontStyle}; + font-weight: ${props => props.fontWeight}; + font-size: ${props => props.fontSize}; + opacity: ${props => props.opacity}; + text-decoration-line: ${props => props.textDecorationLine}; + ${props => (props.lineHeight ? `line-height: ${props.lineHeight}` : '')}; + ${props => (props.center ? 'text-align: center' : '')}; + color: ${props => props.fontColor && props.theme[props.fontColor]}; + ${props => (props.minHeight ? `min-height: ${props.minHeight}` : '')}; + ${props => (props.onClick ? 'cursor: pointer' : '')}; + transition: color 0.5s ease; + ${props => (props.noWrap ? 'white-space: nowrap' : '')}; + ${props => (props.display ? `display: ${props.display}` : '')}; + ${props => (props.letterSpacing ? `letter-spacing: ${props.letterSpacing}` : '')}; + ${props => (props.textTransform ? `text-transform: ${props.textTransform}` : '')}; + &:hover { + ${props => + props.onClick + ? `color: ${props.hoverColor || darken(darkenOnHoverAmount, props.theme[props.fontColor || 'white'])}` + : ''}; + } +`; + +Text.defaultProps = { + fontFamily: 'Inter UI', + fontStyle: 'normal', + fontWeight: 400, + fontColor: ColorOption.black, + fontSize: '15px', + textDecorationLine: 'none', + noWrap: false, + display: 'inline-block', +}; + +Text.displayName = 'Text'; + +export const Title: React.StatelessComponent<TextProps> = props => <Text {...props} />; + +Title.defaultProps = { + fontSize: '20px', + fontWeight: 600, + opacity: 1, + fontColor: ColorOption.primaryColor, +}; + +Title.displayName = 'Title'; diff --git a/packages/instant/src/components/zero_ex_instant.tsx b/packages/instant/src/components/zero_ex_instant.tsx index 67e1b683d..0e6230d1b 100644 --- a/packages/instant/src/components/zero_ex_instant.tsx +++ b/packages/instant/src/components/zero_ex_instant.tsx @@ -1,5 +1,20 @@ import * as React from 'react'; +import { Provider } from 'react-redux'; + +import { store } from '../redux/store'; +import { fonts } from '../style/fonts'; +import { theme, ThemeProvider } from '../style/theme'; + +import { ZeroExInstantContainer } from './zero_ex_instant_container'; + +fonts.include(); export interface ZeroExInstantProps {} -export const ZeroExInstant: React.StatelessComponent<ZeroExInstantProps> = () => <div>ZeroExInstant</div>; +export const ZeroExInstant: React.StatelessComponent<ZeroExInstantProps> = () => ( + <Provider store={store}> + <ThemeProvider theme={theme}> + <ZeroExInstantContainer /> + </ThemeProvider> + </Provider> +); diff --git a/packages/instant/src/components/zero_ex_instant_container.tsx b/packages/instant/src/components/zero_ex_instant_container.tsx new file mode 100644 index 000000000..716227b51 --- /dev/null +++ b/packages/instant/src/components/zero_ex_instant_container.tsx @@ -0,0 +1,20 @@ +import * as React from 'react'; + +import { ColorOption } from '../style/theme'; + +import { BuyButton } from './buy_button'; +import { InstantHeading } from './instant_heading'; +import { OrderDetails } from './order_details'; +import { Container, Flex } from './ui'; + +export interface ZeroExInstantContainerProps {} + +export const ZeroExInstantContainer: React.StatelessComponent<ZeroExInstantContainerProps> = props => ( + <Container hasBoxShadow={true} width="350px" backgroundColor={ColorOption.white} borderRadius="3px"> + <Flex direction="column" justify="flex-start"> + <InstantHeading /> + <OrderDetails /> + <BuyButton /> + </Flex> + </Container> +); diff --git a/packages/instant/src/containers/selected_asset_amount_input.tsx b/packages/instant/src/containers/selected_asset_amount_input.tsx new file mode 100644 index 000000000..800a4c568 --- /dev/null +++ b/packages/instant/src/containers/selected_asset_amount_input.tsx @@ -0,0 +1,36 @@ +import { BigNumber } from '@0xproject/utils'; +import * as React from 'react'; +import { connect } from 'react-redux'; +import { Dispatch } from 'redux'; + +import { State } from '../redux/reducer'; +import { ColorOption } from '../style/theme'; +import { Action, ActionTypes } from '../types'; + +import { AmountInput } from '../components/amount_input'; + +export interface SelectedAssetAmountInputProps { + fontColor?: ColorOption; + fontSize?: string; +} + +interface ConnectedState { + value?: BigNumber; +} + +interface ConnectedDispatch { + onChange?: (value?: BigNumber) => void; +} + +const mapStateToProps = (state: State, _ownProps: SelectedAssetAmountInputProps): ConnectedState => ({ + value: state.selectedAssetAmount, +}); + +const mapDispatchToProps = (dispatch: Dispatch<Action>): ConnectedDispatch => ({ + onChange: value => dispatch({ type: ActionTypes.UPDATE_SELECTED_ASSET_AMOUNT, data: value }), +}); + +export const SelectedAssetAmountInput: React.ComponentClass<SelectedAssetAmountInputProps> = connect( + mapStateToProps, + mapDispatchToProps, +)(AmountInput); diff --git a/packages/instant/src/redux/reducer.ts b/packages/instant/src/redux/reducer.ts new file mode 100644 index 000000000..5026895ae --- /dev/null +++ b/packages/instant/src/redux/reducer.ts @@ -0,0 +1,31 @@ +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; + +import { Action, ActionTypes } from '../types'; + +export interface State { + ethUsdPrice?: string; + selectedAssetAmount?: BigNumber; +} + +export const INITIAL_STATE: State = { + ethUsdPrice: undefined, + selectedAssetAmount: undefined, +}; + +export const reducer = (state: State = INITIAL_STATE, action: Action): State => { + switch (action.type) { + case ActionTypes.UPDATE_ETH_USD_PRICE: + return { + ...state, + ethUsdPrice: action.data, + }; + case ActionTypes.UPDATE_SELECTED_ASSET_AMOUNT: + return { + ...state, + selectedAssetAmount: action.data, + }; + default: + return state; + } +}; diff --git a/packages/instant/src/redux/store.ts b/packages/instant/src/redux/store.ts new file mode 100644 index 000000000..fcd19f9a8 --- /dev/null +++ b/packages/instant/src/redux/store.ts @@ -0,0 +1,6 @@ +import * as _ from 'lodash'; +import { createStore, Store as ReduxStore } from 'redux'; + +import { reducer, State } from './reducer'; + +export const store: ReduxStore<State> = createStore(reducer); diff --git a/packages/instant/src/style/fonts.ts b/packages/instant/src/style/fonts.ts new file mode 100644 index 000000000..975a30a61 --- /dev/null +++ b/packages/instant/src/style/fonts.ts @@ -0,0 +1,10 @@ +import { injectGlobal } from './theme'; + +export const fonts = { + include: () => { + // Inject the inter-ui font into the page + return injectGlobal` + @import url('https://rsms.me/inter/inter-ui.css'); + `; + }, +}; diff --git a/packages/instant/src/style/theme.ts b/packages/instant/src/style/theme.ts new file mode 100644 index 000000000..cf9da5378 --- /dev/null +++ b/packages/instant/src/style/theme.ts @@ -0,0 +1,27 @@ +import * as styledComponents from 'styled-components'; + +const { default: styled, css, injectGlobal, keyframes, ThemeProvider } = styledComponents; + +export type Theme = { [key in ColorOption]: string }; + +export enum ColorOption { + primaryColor = 'primaryColor', + black = 'black', + lightGrey = 'lightGrey', + grey = 'grey', + feintGrey = 'feintGrey', + darkGrey = 'darkGrey', + white = 'white', +} + +export const theme: Theme = { + primaryColor: '#512D80', + black: 'black', + lightGrey: '#999999', + grey: '#666666', + feintGrey: '#DEDEDE', + darkGrey: '#333333', + white: 'white', +}; + +export { styled, css, injectGlobal, keyframes, ThemeProvider }; diff --git a/packages/instant/src/style/util.ts b/packages/instant/src/style/util.ts new file mode 100644 index 000000000..c9df0f834 --- /dev/null +++ b/packages/instant/src/style/util.ts @@ -0,0 +1,11 @@ +import { ObjectMap } from '@0xproject/types'; +import * as _ from 'lodash'; + +export const cssRuleIfExists = (props: ObjectMap<any>, rule: string): string => { + const camelCaseRule = _.camelCase(rule); + const ruleValueIfExists = props[camelCaseRule]; + if (!_.isUndefined(ruleValueIfExists)) { + return `${rule}: ${ruleValueIfExists};`; + } + return ''; +}; diff --git a/packages/instant/src/types.ts b/packages/instant/src/types.ts new file mode 100644 index 000000000..d150bd8ab --- /dev/null +++ b/packages/instant/src/types.ts @@ -0,0 +1,9 @@ +export enum ActionTypes { + UPDATE_ETH_USD_PRICE, + UPDATE_SELECTED_ASSET_AMOUNT, +} + +export interface Action { + type: ActionTypes; + data?: any; +} diff --git a/packages/instant/tslint.json b/packages/instant/tslint.json index ffaefe83a..f71532e5d 100644 --- a/packages/instant/tslint.json +++ b/packages/instant/tslint.json @@ -1,3 +1,7 @@ { - "extends": ["@0xproject/tslint-config"] + "extends": ["@0xproject/tslint-config"], + "rules": { + "custom-no-magic-numbers": false, + "semicolon": [true, "always", "ignore-bound-class-methods"] + } } diff --git a/packages/json-schemas/schemas/eip712_typed_data.ts b/packages/json-schemas/schemas/eip712_typed_data.ts new file mode 100644 index 000000000..31ad74610 --- /dev/null +++ b/packages/json-schemas/schemas/eip712_typed_data.ts @@ -0,0 +1,28 @@ +export const eip712TypedDataSchema = { + id: '/eip712TypedData', + type: 'object', + properties: { + types: { + type: 'object', + properties: { + EIP712Domain: { type: 'array' }, + }, + additionalProperties: { + type: 'array', + items: { + type: 'object', + properties: { + name: { type: 'string' }, + type: { type: 'string' }, + }, + required: ['name', 'type'], + }, + }, + required: ['EIP712Domain'], + }, + primaryType: { type: 'string' }, + domain: { type: 'object' }, + message: { type: 'object' }, + }, + required: ['types', 'primaryType', 'domain', 'message'], +}; diff --git a/packages/json-schemas/schemas/zero_ex_transaction_schema.ts b/packages/json-schemas/schemas/zero_ex_transaction_schema.ts new file mode 100644 index 000000000..7f729b724 --- /dev/null +++ b/packages/json-schemas/schemas/zero_ex_transaction_schema.ts @@ -0,0 +1,10 @@ +export const zeroExTransactionSchema = { + id: '/zeroExTransactionSchema', + properties: { + data: { $ref: '/hexSchema' }, + signerAddress: { $ref: '/addressSchema' }, + salt: { $ref: '/numberSchema' }, + }, + required: ['data', 'salt', 'signerAddress'], + type: 'object', +}; diff --git a/packages/json-schemas/src/schemas.ts b/packages/json-schemas/src/schemas.ts index 3bc37f96b..4eb96092d 100644 --- a/packages/json-schemas/src/schemas.ts +++ b/packages/json-schemas/src/schemas.ts @@ -2,6 +2,7 @@ import { addressSchema, hexSchema, numberSchema } from '../schemas/basic_type_sc import { blockParamSchema, blockRangeSchema } from '../schemas/block_range_schema'; import { callDataSchema } from '../schemas/call_data_schema'; import { ecSignatureParameterSchema, ecSignatureSchema } from '../schemas/ec_signature_schema'; +import { eip712TypedDataSchema } from '../schemas/eip712_typed_data'; import { indexFilterValuesSchema } from '../schemas/index_filter_values_schema'; import { orderCancellationRequestsSchema } from '../schemas/order_cancel_schema'; import { orderFillOrKillRequestsSchema } from '../schemas/order_fill_or_kill_requests_schema'; @@ -31,6 +32,7 @@ import { relayerApiOrdersSchema } from '../schemas/relayer_api_orders_schema'; import { signedOrdersSchema } from '../schemas/signed_orders_schema'; import { tokenSchema } from '../schemas/token_schema'; import { jsNumber, txDataSchema } from '../schemas/tx_data_schema'; +import { zeroExTransactionSchema } from '../schemas/zero_ex_transaction_schema'; export const schemas = { numberSchema, @@ -39,6 +41,7 @@ export const schemas = { hexSchema, ecSignatureParameterSchema, ecSignatureSchema, + eip712TypedDataSchema, indexFilterValuesSchema, orderCancellationRequestsSchema, orderFillOrKillRequestsSchema, @@ -68,4 +71,5 @@ export const schemas = { relayerApiOrdersChannelUpdateSchema, relayerApiOrdersResponseSchema, relayerApiAssetDataPairsSchema, + zeroExTransactionSchema, }; diff --git a/packages/metacoin/package.json b/packages/metacoin/package.json index 83082d964..58fc0b848 100644 --- a/packages/metacoin/package.json +++ b/packages/metacoin/package.json @@ -41,7 +41,7 @@ "@types/mocha": "^5.2.2", "copyfiles": "^2.0.0", "ethereum-types": "^1.0.11", - "ethers": "4.0.0-beta.14", + "ethers": "~4.0.4", "lodash": "^4.17.5", "run-s": "^0.0.0" }, diff --git a/packages/migrations/package.json b/packages/migrations/package.json index dcae2c562..2d1b7bfc2 100644 --- a/packages/migrations/package.json +++ b/packages/migrations/package.json @@ -54,7 +54,7 @@ "@0xproject/web3-wrapper": "^3.0.3", "@ledgerhq/hw-app-eth": "^4.3.0", "ethereum-types": "^1.0.11", - "ethers": "4.0.0-beta.14", + "ethers": "~4.0.4", "lodash": "^4.17.5" }, "optionalDependencies": { diff --git a/packages/order-utils/CHANGELOG.json b/packages/order-utils/CHANGELOG.json index 3e841c43c..5a0c0db47 100644 --- a/packages/order-utils/CHANGELOG.json +++ b/packages/order-utils/CHANGELOG.json @@ -1,5 +1,23 @@ [ { + "version": "2.0.0", + "changes": [ + { + "note": + "Added `ecSignOrderAsync` to first sign an order using `eth_signTypedData` and fallback to `eth_sign`.", + "pr": 1102 + }, + { + "note": "Added `ecSignTypedDataOrderAsync` to sign an order exclusively using `eth_signTypedData`.", + "pr": 1102 + }, + { + "note": "Rename `ecSignOrderHashAsync` to `ecSignHashAsync` removing `SignerType` parameter.", + "pr": 1102 + } + ] + }, + { "version": "1.0.7", "changes": [ { diff --git a/packages/order-utils/package.json b/packages/order-utils/package.json index 23ed9ca12..6471de9f5 100644 --- a/packages/order-utils/package.json +++ b/packages/order-utils/package.json @@ -70,7 +70,7 @@ "ethereum-types": "^1.0.11", "ethereumjs-abi": "0.6.5", "ethereumjs-util": "^5.1.1", - "ethers": "4.0.0-beta.14", + "ethers": "~4.0.4", "lodash": "^4.17.5" }, "publishConfig": { diff --git a/packages/order-utils/src/constants.ts b/packages/order-utils/src/constants.ts index c23578c20..7de20a696 100644 --- a/packages/order-utils/src/constants.ts +++ b/packages/order-utils/src/constants.ts @@ -13,4 +13,39 @@ export const constants = { BASE_16: 16, INFINITE_TIMESTAMP_SEC: new BigNumber(2524604400), // Close to infinite ZERO_AMOUNT: new BigNumber(0), + EIP712_DOMAIN_NAME: '0x Protocol', + EIP712_DOMAIN_VERSION: '2', + EIP712_DOMAIN_SCHEMA: { + name: 'EIP712Domain', + parameters: [ + { name: 'name', type: 'string' }, + { name: 'version', type: 'string' }, + { name: 'verifyingContract', type: 'address' }, + ], + }, + EIP712_ORDER_SCHEMA: { + name: 'Order', + parameters: [ + { name: 'makerAddress', type: 'address' }, + { name: 'takerAddress', type: 'address' }, + { name: 'feeRecipientAddress', type: 'address' }, + { name: 'senderAddress', type: 'address' }, + { name: 'makerAssetAmount', type: 'uint256' }, + { name: 'takerAssetAmount', type: 'uint256' }, + { name: 'makerFee', type: 'uint256' }, + { name: 'takerFee', type: 'uint256' }, + { name: 'expirationTimeSeconds', type: 'uint256' }, + { name: 'salt', type: 'uint256' }, + { name: 'makerAssetData', type: 'bytes' }, + { name: 'takerAssetData', type: 'bytes' }, + ], + }, + EIP712_ZEROEX_TRANSACTION_SCHEMA: { + name: 'ZeroExTransaction', + parameters: [ + { name: 'salt', type: 'uint256' }, + { name: 'signerAddress', type: 'address' }, + { name: 'data', type: 'bytes' }, + ], + }, }; diff --git a/packages/order-utils/src/eip712_utils.ts b/packages/order-utils/src/eip712_utils.ts index b303c93dc..56f736500 100644 --- a/packages/order-utils/src/eip712_utils.ts +++ b/packages/order-utils/src/eip712_utils.ts @@ -1,109 +1,83 @@ -import ethUtil = require('ethereumjs-util'); +import { assert } from '@0xproject/assert'; +import { schemas } from '@0xproject/json-schemas'; +import { EIP712Object, EIP712TypedData, EIP712Types, Order, ZeroExTransaction } from '@0xproject/types'; import * as _ from 'lodash'; -import { crypto } from './crypto'; -import { EIP712Schema, EIP712Types } from './types'; - -const EIP191_PREFIX = '\x19\x01'; -const EIP712_DOMAIN_NAME = '0x Protocol'; -const EIP712_DOMAIN_VERSION = '2'; -const EIP712_VALUE_LENGTH = 32; - -const EIP712_DOMAIN_SCHEMA: EIP712Schema = { - name: 'EIP712Domain', - parameters: [ - { name: 'name', type: EIP712Types.String }, - { name: 'version', type: EIP712Types.String }, - { name: 'verifyingContract', type: EIP712Types.Address }, - ], -}; +import { constants } from './constants'; export const eip712Utils = { /** - * Compiles the EIP712Schema and returns the hash of the schema. - * @param schema The EIP712 schema. - * @return The hash of the compiled schema - */ - compileSchema(schema: EIP712Schema): Buffer { - const eip712Schema = eip712Utils._encodeType(schema); - const eip712SchemaHashBuffer = crypto.solSHA3([eip712Schema]); - return eip712SchemaHashBuffer; - }, - /** - * Merges the EIP712 hash of a struct with the DomainSeparator for 0x v2. - * @param hashStruct the EIP712 hash of a struct - * @param contractAddress the exchange contract address - * @return The hash of an EIP712 message with domain separator prefixed - */ - createEIP712Message(hashStruct: Buffer, contractAddress: string): Buffer { - const domainSeparatorHashBuffer = eip712Utils._getDomainSeparatorHashBuffer(contractAddress); - const messageBuff = crypto.solSHA3([EIP191_PREFIX, domainSeparatorHashBuffer, hashStruct]); - return messageBuff; - }, - /** - * Pad an address to 32 bytes - * @param address Address to pad - * @return padded address + * Creates a EIP712TypedData object specific to the 0x protocol for use with signTypedData. + * @param primaryType The primary type found in message + * @param types The additional types for the data in message + * @param message The contents of the message + * @param exchangeAddress The address of the exchange contract + * @return A typed data object */ - pad32Address(address: string): Buffer { - const addressBuffer = ethUtil.toBuffer(address); - const addressPadded = eip712Utils.pad32Buffer(addressBuffer); - return addressPadded; + createTypedData: ( + primaryType: string, + types: EIP712Types, + message: EIP712Object, + exchangeAddress: string, + ): EIP712TypedData => { + assert.isETHAddressHex('exchangeAddress', exchangeAddress); + assert.isString('primaryType', primaryType); + const typedData = { + types: { + EIP712Domain: constants.EIP712_DOMAIN_SCHEMA.parameters, + ...types, + }, + domain: { + name: constants.EIP712_DOMAIN_NAME, + version: constants.EIP712_DOMAIN_VERSION, + verifyingContract: exchangeAddress, + }, + message, + primaryType, + }; + assert.doesConformToSchema('typedData', typedData, schemas.eip712TypedDataSchema); + return typedData; }, /** - * Pad an buffer to 32 bytes - * @param buffer Address to pad - * @return padded buffer + * Creates an Order EIP712TypedData object for use with signTypedData. + * @param Order the order + * @return A typed data object */ - pad32Buffer(buffer: Buffer): Buffer { - const bufferPadded = ethUtil.setLengthLeft(buffer, EIP712_VALUE_LENGTH); - return bufferPadded; + createOrderTypedData: (order: Order): EIP712TypedData => { + assert.doesConformToSchema('order', order, schemas.orderSchema, [schemas.hexSchema]); + const normalizedOrder = _.mapValues(order, value => { + return !_.isString(value) ? value.toString() : value; + }); + const typedData = eip712Utils.createTypedData( + constants.EIP712_ORDER_SCHEMA.name, + { Order: constants.EIP712_ORDER_SCHEMA.parameters }, + normalizedOrder, + order.exchangeAddress, + ); + return typedData; }, /** - * Hash together a EIP712 schema with the corresponding data - * @param schema EIP712-compliant schema - * @param data Data the complies to the schema - * @return A buffer containing the SHA256 hash of the schema and encoded data + * Creates an ExecuteTransaction EIP712TypedData object for use with signTypedData and + * 0x Exchange executeTransaction. + * @param ZeroExTransaction the 0x transaction + * @param exchangeAddress The address of the exchange contract + * @return A typed data object */ - structHash(schema: EIP712Schema, data: { [key: string]: any }): Buffer { - const encodedData = eip712Utils._encodeData(schema, data); - const schemaHash = eip712Utils.compileSchema(schema); - const hashBuffer = crypto.solSHA3([schemaHash, ...encodedData]); - return hashBuffer; - }, - _getDomainSeparatorSchemaBuffer(): Buffer { - return eip712Utils.compileSchema(EIP712_DOMAIN_SCHEMA); - }, - _getDomainSeparatorHashBuffer(exchangeAddress: string): Buffer { - const domainSeparatorSchemaBuffer = eip712Utils._getDomainSeparatorSchemaBuffer(); - const encodedData = eip712Utils._encodeData(EIP712_DOMAIN_SCHEMA, { - name: EIP712_DOMAIN_NAME, - version: EIP712_DOMAIN_VERSION, - verifyingContract: exchangeAddress, + createZeroExTransactionTypedData: ( + zeroExTransaction: ZeroExTransaction, + exchangeAddress: string, + ): EIP712TypedData => { + assert.isETHAddressHex('exchangeAddress', exchangeAddress); + assert.doesConformToSchema('zeroExTransaction', zeroExTransaction, schemas.zeroExTransactionSchema); + const normalizedTransaction = _.mapValues(zeroExTransaction, value => { + return !_.isString(value) ? value.toString() : value; }); - const domainSeparatorHashBuff2 = crypto.solSHA3([domainSeparatorSchemaBuffer, ...encodedData]); - return domainSeparatorHashBuff2; - }, - _encodeType(schema: EIP712Schema): string { - const namedTypes = _.map(schema.parameters, ({ name, type }) => `${type} ${name}`); - const namedTypesJoined = namedTypes.join(','); - const encodedType = `${schema.name}(${namedTypesJoined})`; - return encodedType; - }, - _encodeData(schema: EIP712Schema, data: { [key: string]: any }): any { - const encodedValues = []; - for (const parameter of schema.parameters) { - const value = data[parameter.name]; - if (parameter.type === EIP712Types.String || parameter.type === EIP712Types.Bytes) { - encodedValues.push(crypto.solSHA3([ethUtil.toBuffer(value)])); - } else if (parameter.type === EIP712Types.Uint256) { - encodedValues.push(value); - } else if (parameter.type === EIP712Types.Address) { - encodedValues.push(eip712Utils.pad32Address(value)); - } else { - throw new Error(`Unable to encode ${parameter.type}`); - } - } - return encodedValues; + const typedData = eip712Utils.createTypedData( + constants.EIP712_ZEROEX_TRANSACTION_SCHEMA.name, + { ZeroExTransaction: constants.EIP712_ZEROEX_TRANSACTION_SCHEMA.parameters }, + normalizedTransaction, + exchangeAddress, + ); + return typedData; }, }; diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts index 1553647c6..dbb782b85 100644 --- a/packages/order-utils/src/index.ts +++ b/packages/order-utils/src/index.ts @@ -2,7 +2,6 @@ export { orderHashUtils } from './order_hash'; export { signatureUtils } from './signature_utils'; export { generatePseudoRandomSalt } from './salt'; export { assetDataUtils } from './asset_data_utils'; -export { eip712Utils } from './eip712_utils'; export { marketUtils } from './market_utils'; export { rateUtils } from './rate_utils'; export { sortingUtils } from './sorting_utils'; @@ -19,7 +18,17 @@ export { ExchangeTransferSimulator } from './exchange_transfer_simulator'; export { BalanceAndProxyAllowanceLazyStore } from './store/balance_and_proxy_allowance_lazy_store'; export { OrderFilledCancelledLazyStore } from './store/order_filled_cancelled_lazy_store'; -export { Provider, JSONRPCRequestPayload, JSONRPCErrorCallback, JSONRPCResponsePayload } from 'ethereum-types'; +export { constants } from './constants'; +export { eip712Utils } from './eip712_utils'; + +export { + Provider, + JSONRPCRequestPayload, + JSONRPCErrorCallback, + JSONRPCResponsePayload, + JSONRPCResponseError, +} from 'ethereum-types'; + export { SignedOrder, Order, @@ -29,17 +38,19 @@ export { ERC20AssetData, ERC721AssetData, AssetProxyId, - SignerType, SignatureType, OrderStateValid, OrderStateInvalid, ExchangeContractErrs, + EIP712Parameter, + EIP712TypedData, + EIP712Types, + EIP712Object, + EIP712ObjectValue, + ZeroExTransaction, } from '@0xproject/types'; export { OrderError, - EIP712Parameter, - EIP712Schema, - EIP712Types, TradeSide, TransferType, FindFeeOrdersThatCoverFeesForTargetOrdersOpts, diff --git a/packages/order-utils/src/order_factory.ts b/packages/order-utils/src/order_factory.ts index b1292903a..0f0cd6046 100644 --- a/packages/order-utils/src/order_factory.ts +++ b/packages/order-utils/src/order_factory.ts @@ -1,4 +1,4 @@ -import { Order, SignedOrder, SignerType } from '@0xproject/types'; +import { Order, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Provider } from 'ethereum-types'; import * as _ from 'lodash'; @@ -71,12 +71,7 @@ export const orderFactory = { createOrderOpts, ); const orderHash = orderHashUtils.getOrderHashHex(order); - const signature = await signatureUtils.ecSignOrderHashAsync( - provider, - orderHash, - makerAddress, - SignerType.Default, - ); + const signature = await signatureUtils.ecSignHashAsync(provider, orderHash, makerAddress); const signedOrder: SignedOrder = _.assign(order, { signature }); return signedOrder; }, diff --git a/packages/order-utils/src/order_hash.ts b/packages/order-utils/src/order_hash.ts index 8e98f8767..b523a3523 100644 --- a/packages/order-utils/src/order_hash.ts +++ b/packages/order-utils/src/order_hash.ts @@ -1,31 +1,13 @@ import { schemas, SchemaValidator } from '@0xproject/json-schemas'; import { Order, SignedOrder } from '@0xproject/types'; +import { signTypedDataUtils } from '@0xproject/utils'; import * as _ from 'lodash'; import { assert } from './assert'; import { eip712Utils } from './eip712_utils'; -import { EIP712Schema, EIP712Types } from './types'; const INVALID_TAKER_FORMAT = 'instance.takerAddress is not of a type(s) string'; -const EIP712_ORDER_SCHEMA: EIP712Schema = { - name: 'Order', - parameters: [ - { name: 'makerAddress', type: EIP712Types.Address }, - { name: 'takerAddress', type: EIP712Types.Address }, - { name: 'feeRecipientAddress', type: EIP712Types.Address }, - { name: 'senderAddress', type: EIP712Types.Address }, - { name: 'makerAssetAmount', type: EIP712Types.Uint256 }, - { name: 'takerAssetAmount', type: EIP712Types.Uint256 }, - { name: 'makerFee', type: EIP712Types.Uint256 }, - { name: 'takerFee', type: EIP712Types.Uint256 }, - { name: 'expirationTimeSeconds', type: EIP712Types.Uint256 }, - { name: 'salt', type: EIP712Types.Uint256 }, - { name: 'makerAssetData', type: EIP712Types.Bytes }, - { name: 'takerAssetData', type: EIP712Types.Bytes }, - ], -}; - export const orderHashUtils = { /** * Checks if the supplied hex encoded order hash is valid. @@ -45,7 +27,7 @@ export const orderHashUtils = { /** * Computes the orderHash for a supplied order. * @param order An object that conforms to the Order or SignedOrder interface definitions. - * @return The resulting orderHash from hashing the supplied order. + * @return Hex encoded string orderHash from hashing the supplied order. */ getOrderHashHex(order: SignedOrder | Order): string { try { @@ -64,16 +46,13 @@ export const orderHashUtils = { return orderHashHex; }, /** - * Computes the orderHash for a supplied order and returns it as a Buffer + * Computes the orderHash for a supplied order * @param order An object that conforms to the Order or SignedOrder interface definitions. - * @return The resulting orderHash from hashing the supplied order as a Buffer + * @return A Buffer containing the resulting orderHash from hashing the supplied order */ getOrderHashBuffer(order: SignedOrder | Order): Buffer { - const orderParamsHashBuff = eip712Utils.structHash(EIP712_ORDER_SCHEMA, order); - const orderHashBuff = eip712Utils.createEIP712Message(orderParamsHashBuff, order.exchangeAddress); + const typedData = eip712Utils.createOrderTypedData(order); + const orderHashBuff = signTypedDataUtils.generateTypedDataHash(typedData); return orderHashBuff; }, - _getOrderSchemaBuffer(): Buffer { - return eip712Utils.compileSchema(EIP712_ORDER_SCHEMA); - }, }; diff --git a/packages/order-utils/src/signature_utils.ts b/packages/order-utils/src/signature_utils.ts index 3b656d3fc..372d210d0 100644 --- a/packages/order-utils/src/signature_utils.ts +++ b/packages/order-utils/src/signature_utils.ts @@ -1,5 +1,5 @@ import { schemas } from '@0xproject/json-schemas'; -import { ECSignature, SignatureType, SignerType, ValidatorSignature } from '@0xproject/types'; +import { ECSignature, Order, SignatureType, SignedOrder, ValidatorSignature } from '@0xproject/types'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import { Provider } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; @@ -7,9 +7,11 @@ import * as _ from 'lodash'; import { artifacts } from './artifacts'; import { assert } from './assert'; +import { eip712Utils } from './eip712_utils'; import { ExchangeContract } from './generated_contract_wrappers/exchange'; import { IValidatorContract } from './generated_contract_wrappers/i_validator'; import { IWalletContract } from './generated_contract_wrappers/i_wallet'; +import { orderHashUtils } from './order_hash'; import { OrderError } from './types'; import { utils } from './utils'; @@ -49,7 +51,7 @@ export const signatureUtils = { case SignatureType.EthSign: { const ecSignature = signatureUtils.parseECSignature(signature); - const prefixedMessageHex = signatureUtils.addSignedMessagePrefix(data, SignerType.Default); + const prefixedMessageHex = signatureUtils.addSignedMessagePrefix(data); return signatureUtils.isValidECSignature(prefixedMessageHex, ecSignature, signerAddress); } @@ -192,36 +194,90 @@ export const signatureUtils = { } }, /** - * Signs an orderHash and returns it's elliptic curve signature and signature type. - * This method currently supports TestRPC, Geth and Parity above and below V1.6.6 - * @param orderHash Hex encoded orderHash to sign. + * Signs an order and returns a SignedOrder. First `eth_signTypedData` is requested + * then a fallback to `eth_sign` if not available on the supplied provider. + * @param order The Order to sign. * @param signerAddress The hex encoded Ethereum address you wish to sign it with. This address - * must be available via the Provider supplied to 0x.js. - * @param signerType Different signers add/require different prefixes to be prepended to the message being signed. - * Since we cannot know ahead of time which signer you are using, you must supply a SignerType. - * @return A hex encoded string containing the Elliptic curve signature generated by signing the orderHash and the Signature Type. + * must be available via the supplied Provider. + * @return A SignedOrder containing the order and Elliptic curve signature with Signature Type. */ - async ecSignOrderHashAsync( - provider: Provider, - orderHash: string, - signerAddress: string, - signerType: SignerType, - ): Promise<string> { + async ecSignOrderAsync(provider: Provider, order: Order, signerAddress: string): Promise<SignedOrder> { + assert.doesConformToSchema('order', order, schemas.orderSchema, [schemas.hexSchema]); + try { + const signedOrder = await signatureUtils.ecSignTypedDataOrderAsync(provider, order, signerAddress); + return signedOrder; + } catch (err) { + // HACK: We are unable to handle specific errors thrown since provider is not an object + // under our control. It could be Metamask Web3, Ethers, or any general RPC provider. + // We check for a user denying the signature request in a way that supports Metamask and + // Coinbase Wallet. Unfortunately for signers with a different error message, + // they will receive two signature requests. + if (err.message.includes('User denied message signature')) { + throw err; + } + const orderHash = orderHashUtils.getOrderHashHex(order); + const signatureHex = await signatureUtils.ecSignHashAsync(provider, orderHash, signerAddress); + const signedOrder = { + ...order, + signature: signatureHex, + }; + return signedOrder; + } + }, + /** + * Signs an order using `eth_signTypedData` and returns a SignedOrder. + * @param order The Order to sign. + * @param signerAddress The hex encoded Ethereum address you wish to sign it with. This address + * must be available via the supplied Provider. + * @return A SignedOrder containing the order and Elliptic curve signature with Signature Type. + */ + async ecSignTypedDataOrderAsync(provider: Provider, order: Order, signerAddress: string): Promise<SignedOrder> { assert.isWeb3Provider('provider', provider); - assert.isHexString('orderHash', orderHash); assert.isETHAddressHex('signerAddress', signerAddress); + assert.doesConformToSchema('order', order, schemas.orderSchema, [schemas.hexSchema]); const web3Wrapper = new Web3Wrapper(provider); await assert.isSenderAddressAsync('signerAddress', signerAddress, web3Wrapper); const normalizedSignerAddress = signerAddress.toLowerCase(); - - let msgHashHex = orderHash; - const prefixedMsgHashHex = signatureUtils.addSignedMessagePrefix(orderHash, signerType); - // Metamask incorrectly implements eth_sign and does not prefix the message as per the spec - // Source: https://github.com/MetaMask/metamask-extension/commit/a9d36860bec424dcee8db043d3e7da6a5ff5672e - if (signerType === SignerType.Metamask) { - msgHashHex = prefixedMsgHashHex; + const typedData = eip712Utils.createOrderTypedData(order); + try { + const signature = await web3Wrapper.signTypedDataAsync(normalizedSignerAddress, typedData); + const ecSignatureRSV = parseSignatureHexAsRSV(signature); + const signatureBuffer = Buffer.concat([ + ethUtil.toBuffer(ecSignatureRSV.v), + ethUtil.toBuffer(ecSignatureRSV.r), + ethUtil.toBuffer(ecSignatureRSV.s), + ethUtil.toBuffer(SignatureType.EIP712), + ]); + const signatureHex = `0x${signatureBuffer.toString('hex')}`; + return { + ...order, + signature: signatureHex, + }; + } catch (err) { + // Detect if Metamask to transition users to the MetamaskSubprovider + if ((provider as any).isMetaMask) { + throw new Error(OrderError.InvalidMetamaskSigner); + } else { + throw err; + } } - const signature = await web3Wrapper.signMessageAsync(normalizedSignerAddress, msgHashHex); + }, + /** + * Signs a hash using `eth_sign` and returns its elliptic curve signature and signature type. + * @param msgHash Hex encoded message to sign. + * @param signerAddress The hex encoded Ethereum address you wish to sign it with. This address + * must be available via the supplied Provider. + * @return A hex encoded string containing the Elliptic curve signature generated by signing the msgHash and the Signature Type. + */ + async ecSignHashAsync(provider: Provider, msgHash: string, signerAddress: string): Promise<string> { + assert.isWeb3Provider('provider', provider); + assert.isHexString('msgHash', msgHash); + assert.isETHAddressHex('signerAddress', signerAddress); + const web3Wrapper = new Web3Wrapper(provider); + await assert.isSenderAddressAsync('signerAddress', signerAddress, web3Wrapper); + const normalizedSignerAddress = signerAddress.toLowerCase(); + const signature = await web3Wrapper.signMessageAsync(normalizedSignerAddress, msgHash); + const prefixedMsgHashHex = signatureUtils.addSignedMessagePrefix(msgHash); // HACK: There is no consensus on whether the signatureHex string should be formatted as // v + r + s OR r + s + v, and different clients (even different versions of the same client) @@ -238,10 +294,7 @@ export const signatureUtils = { normalizedSignerAddress, ); if (isValidRSVSignature) { - const convertedSignatureHex = signatureUtils.convertECSignatureToSignatureHex( - ecSignatureRSV, - signerType, - ); + const convertedSignatureHex = signatureUtils.convertECSignatureToSignatureHex(ecSignatureRSV); return convertedSignatureHex; } } @@ -253,41 +306,30 @@ export const signatureUtils = { normalizedSignerAddress, ); if (isValidVRSSignature) { - const convertedSignatureHex = signatureUtils.convertECSignatureToSignatureHex( - ecSignatureVRS, - signerType, - ); + const convertedSignatureHex = signatureUtils.convertECSignatureToSignatureHex(ecSignatureVRS); return convertedSignatureHex; } } - - throw new Error(OrderError.InvalidSignature); + // Detect if Metamask to transition users to the MetamaskSubprovider + if ((provider as any).isMetaMask) { + throw new Error(OrderError.InvalidMetamaskSigner); + } else { + throw new Error(OrderError.InvalidSignature); + } }, /** - * Combines ECSignature with V,R,S and the relevant signature type for use in 0x protocol + * Combines ECSignature with V,R,S and the EthSign signature type for use in 0x protocol * @param ecSignature The ECSignature of the signed data - * @param signerType The SignerType of the signed data * @return Hex encoded string of signature (v,r,s) with Signature Type */ - convertECSignatureToSignatureHex(ecSignature: ECSignature, signerType: SignerType): string { + convertECSignatureToSignatureHex(ecSignature: ECSignature): string { const signatureBuffer = Buffer.concat([ ethUtil.toBuffer(ecSignature.v), ethUtil.toBuffer(ecSignature.r), ethUtil.toBuffer(ecSignature.s), ]); const signatureHex = `0x${signatureBuffer.toString('hex')}`; - let signatureType; - switch (signerType) { - case SignerType.Metamask: - case SignerType.Ledger: - case SignerType.Default: { - signatureType = SignatureType.EthSign; - break; - } - default: - throw new Error(`Unrecognized SignerType: ${signerType}`); - } - const signatureWithType = signatureUtils.convertToSignatureWithType(signatureHex, signatureType); + const signatureWithType = signatureUtils.convertToSignatureWithType(signatureHex, SignatureType.EthSign); return signatureWithType; }, /** @@ -304,28 +346,17 @@ export const signatureUtils = { /** * Adds the relevant prefix to the message being signed. * @param message Message to sign - * @param signerType The type of message prefix to add for a given SignerType. Different signers expect - * specific message prefixes. * @return Prefixed message */ - addSignedMessagePrefix(message: string, signerType: SignerType = SignerType.Default): string { + addSignedMessagePrefix(message: string): string { assert.isString('message', message); - assert.doesBelongToStringEnum('signerType', signerType, SignerType); - switch (signerType) { - case SignerType.Metamask: - case SignerType.Ledger: - case SignerType.Default: { - const msgBuff = ethUtil.toBuffer(message); - const prefixedMsgBuff = ethUtil.hashPersonalMessage(msgBuff); - const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff); - return prefixedMsgHex; - } - default: - throw new Error(`Unrecognized SignerType: ${signerType}`); - } + const msgBuff = ethUtil.toBuffer(message); + const prefixedMsgBuff = ethUtil.hashPersonalMessage(msgBuff); + const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff); + return prefixedMsgHex; }, /** - * Parse a 0x protocol hex-encoded signature string into it's ECSignature components + * Parse a 0x protocol hex-encoded signature string into its ECSignature components * @param signature A hex encoded ecSignature 0x Protocol signature * @return An ECSignature object with r,s,v parameters */ diff --git a/packages/order-utils/src/types.ts b/packages/order-utils/src/types.ts index a843efaa4..5b13dd754 100644 --- a/packages/order-utils/src/types.ts +++ b/packages/order-utils/src/types.ts @@ -2,6 +2,7 @@ import { BigNumber } from '@0xproject/utils'; export enum OrderError { InvalidSignature = 'INVALID_SIGNATURE', + InvalidMetamaskSigner = "MetaMask provider must be wrapped in a MetamaskSubprovider (from the '@0xproject/subproviders' package) in order to work with this method.", } export enum TradeSide { @@ -14,24 +15,6 @@ export enum TransferType { Fee = 'fee', } -export interface EIP712Parameter { - name: string; - type: EIP712Types; -} - -export interface EIP712Schema { - name: string; - parameters: EIP712Parameter[]; -} - -export enum EIP712Types { - Address = 'address', - Bytes = 'bytes', - Bytes32 = 'bytes32', - String = 'string', - Uint256 = 'uint256', -} - export interface CreateOrderOpts { takerAddress?: string; senderAddress?: string; diff --git a/packages/order-utils/test/eip712_utils_test.ts b/packages/order-utils/test/eip712_utils_test.ts new file mode 100644 index 000000000..d65cabe9c --- /dev/null +++ b/packages/order-utils/test/eip712_utils_test.ts @@ -0,0 +1,44 @@ +import { BigNumber } from '@0xproject/utils'; +import * as chai from 'chai'; +import 'mocha'; + +import { constants } from '../src/constants'; +import { eip712Utils } from '../src/eip712_utils'; + +import { chaiSetup } from './utils/chai_setup'; + +chaiSetup.configure(); +const expect = chai.expect; + +describe('EIP712 Utils', () => { + describe('createTypedData', () => { + it('adds in the EIP712DomainSeparator', () => { + const primaryType = 'Test'; + const typedData = eip712Utils.createTypedData( + primaryType, + { Test: [{ name: 'testValue', type: 'uint256' }] }, + { testValue: '1' }, + constants.NULL_ADDRESS, + ); + expect(typedData.domain).to.not.be.undefined(); + expect(typedData.types.EIP712Domain).to.not.be.undefined(); + const domainObject = typedData.domain; + expect(domainObject.name).to.eq(constants.EIP712_DOMAIN_NAME); + expect(typedData.primaryType).to.eq(primaryType); + }); + }); + describe('createTypedData', () => { + it('adds in the EIP712DomainSeparator', () => { + const typedData = eip712Utils.createZeroExTransactionTypedData( + { + salt: new BigNumber('0'), + data: constants.NULL_BYTES, + signerAddress: constants.NULL_ADDRESS, + }, + constants.NULL_ADDRESS, + ); + expect(typedData.primaryType).to.eq(constants.EIP712_ZEROEX_TRANSACTION_SCHEMA.name); + expect(typedData.types.EIP712Domain).to.not.be.undefined(); + }); + }); +}); diff --git a/packages/order-utils/test/order_hash_test.ts b/packages/order-utils/test/order_hash_test.ts index 3fdbbad21..fe44218d6 100644 --- a/packages/order-utils/test/order_hash_test.ts +++ b/packages/order-utils/test/order_hash_test.ts @@ -35,6 +35,20 @@ describe('Order hashing', () => { const orderHash = orderHashUtils.getOrderHashHex(order); expect(orderHash).to.be.equal(expectedOrderHash); }); + it('calculates the order hash if amounts are strings', async () => { + // It's common for developers using javascript to provide the amounts + // as strings. Since we eventually toString() the BigNumber + // before encoding we should result in the same orderHash in this scenario + // tslint:disable-next-line:no-unnecessary-type-assertion + const orderHash = orderHashUtils.getOrderHashHex({ + ...order, + makerAssetAmount: '0', + takerAssetAmount: '0', + makerFee: '0', + takerFee: '0', + } as any); + expect(orderHash).to.be.equal(expectedOrderHash); + }); it('throws a readable error message if taker format is invalid', async () => { const orderWithInvalidtakerFormat = { ...order, diff --git a/packages/order-utils/test/signature_utils_test.ts b/packages/order-utils/test/signature_utils_test.ts index 2ca1109a1..f2d6790fb 100644 --- a/packages/order-utils/test/signature_utils_test.ts +++ b/packages/order-utils/test/signature_utils_test.ts @@ -1,12 +1,13 @@ -import { SignerType } from '@0xproject/types'; +import { Order, SignatureType } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; import { JSONRPCErrorCallback, JSONRPCRequestPayload } from 'ethereum-types'; +import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; import 'mocha'; -import * as Sinon from 'sinon'; -import { generatePseudoRandomSalt } from '../src'; +import { generatePseudoRandomSalt, orderHashUtils } from '../src'; +import { constants } from '../src/constants'; import { signatureUtils } from '../src/signature_utils'; import { chaiSetup } from './utils/chai_setup'; @@ -16,6 +17,28 @@ chaiSetup.configure(); const expect = chai.expect; describe('Signature utils', () => { + let makerAddress: string; + const fakeExchangeContractAddress = '0x1dc4c1cefef38a777b15aa20260a54e584b16c48'; + let order: Order; + before(async () => { + const availableAddreses = await web3Wrapper.getAvailableAddressesAsync(); + makerAddress = availableAddreses[0]; + order = { + makerAddress, + takerAddress: constants.NULL_ADDRESS, + senderAddress: constants.NULL_ADDRESS, + feeRecipientAddress: constants.NULL_ADDRESS, + makerAssetData: constants.NULL_ADDRESS, + takerAssetData: constants.NULL_ADDRESS, + exchangeAddress: fakeExchangeContractAddress, + salt: new BigNumber(0), + makerFee: new BigNumber(0), + takerFee: new BigNumber(0), + makerAssetAmount: new BigNumber(0), + takerAssetAmount: new BigNumber(0), + expirationTimeSeconds: new BigNumber(0), + }; + }); describe('#isValidSignatureAsync', () => { let dataHex = '0x6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222b0'; const ethSignSignature = @@ -115,28 +138,64 @@ describe('Signature utils', () => { expect(salt.lessThan(twoPow256)).to.be.true(); }); }); - describe('#ecSignOrderHashAsync', () => { - let stubs: Sinon.SinonStub[] = []; - let makerAddress: string; + describe('#ecSignOrderAsync', () => { + it('should default to eth_sign if eth_signTypedData is unavailable', async () => { + const expectedSignature = + '0x1c3582f06356a1314dbf1c0e534c4d8e92e59b056ee607a7ff5a825f5f2cc5e6151c5cc7fdd420f5608e4d5bef108e42ad90c7a4b408caef32e24374cf387b0d7603'; + + const fakeProvider = { + async sendAsync(payload: JSONRPCRequestPayload, callback: JSONRPCErrorCallback): Promise<void> { + if (payload.method === 'eth_signTypedData') { + callback(new Error('Internal RPC Error')); + } else if (payload.method === 'eth_sign') { + const [address, message] = payload.params; + const signature = await web3Wrapper.signMessageAsync(address, message); + callback(null, { + id: 42, + jsonrpc: '2.0', + result: signature, + }); + } else { + callback(null, { id: 42, jsonrpc: '2.0', result: [makerAddress] }); + } + }, + }; + const signedOrder = await signatureUtils.ecSignOrderAsync(fakeProvider, order, makerAddress); + expect(signedOrder.signature).to.equal(expectedSignature); + }); + it('should throw if the user denies the signing request', async () => { + const fakeProvider = { + async sendAsync(payload: JSONRPCRequestPayload, callback: JSONRPCErrorCallback): Promise<void> { + if (payload.method === 'eth_signTypedData') { + callback(new Error('User denied message signature')); + } else if (payload.method === 'eth_sign') { + const [address, message] = payload.params; + const signature = await web3Wrapper.signMessageAsync(address, message); + callback(null, { + id: 42, + jsonrpc: '2.0', + result: signature, + }); + } else { + callback(null, { id: 42, jsonrpc: '2.0', result: [makerAddress] }); + } + }, + }; + expect(signatureUtils.ecSignOrderAsync(fakeProvider, order, makerAddress)).to.to.be.rejectedWith( + 'User denied message signature', + ); + }); + }); + describe('#ecSignHashAsync', () => { before(async () => { const availableAddreses = await web3Wrapper.getAvailableAddressesAsync(); makerAddress = availableAddreses[0]; }); - afterEach(() => { - // clean up any stubs after the test has completed - _.each(stubs, s => s.restore()); - stubs = []; - }); - it('Should return the correct Signature', async () => { + it('should return the correct Signature', async () => { const orderHash = '0x6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222b0'; const expectedSignature = '0x1b61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc3340349190569279751135161d22529dc25add4f6069af05be04cacbda2ace225403'; - const ecSignature = await signatureUtils.ecSignOrderHashAsync( - provider, - orderHash, - makerAddress, - SignerType.Default, - ); + const ecSignature = await signatureUtils.ecSignHashAsync(provider, orderHash, makerAddress); expect(ecSignature).to.equal(expectedSignature); }); it('should return the correct Signature for signatureHex concatenated as R + S + V', async () => { @@ -162,12 +221,7 @@ describe('Signature utils', () => { } }, }; - const ecSignature = await signatureUtils.ecSignOrderHashAsync( - fakeProvider, - orderHash, - makerAddress, - SignerType.Default, - ); + const ecSignature = await signatureUtils.ecSignHashAsync(fakeProvider, orderHash, makerAddress); expect(ecSignature).to.equal(expectedSignature); }); it('should return the correct Signature for signatureHex concatenated as V + R + S', async () => { @@ -190,64 +244,68 @@ describe('Signature utils', () => { }, }; - const ecSignature = await signatureUtils.ecSignOrderHashAsync( - fakeProvider, + const ecSignature = await signatureUtils.ecSignHashAsync(fakeProvider, orderHash, makerAddress); + expect(ecSignature).to.equal(expectedSignature); + }); + it('should return a valid signature', async () => { + const orderHash = '0x34decbedc118904df65f379a175bb39ca18209d6ce41d5ed549d54e6e0a95004'; + const ecSignature = await signatureUtils.ecSignHashAsync(provider, orderHash, makerAddress); + + const isValidSignature = await signatureUtils.isValidSignatureAsync( + provider, orderHash, + ecSignature, makerAddress, - SignerType.Default, ); - expect(ecSignature).to.equal(expectedSignature); + expect(isValidSignature).to.be.true(); }); - // Note this is due to a bug in Metamask where it does not prefix before signing, this is a known issue and is to be fixed in the future - // Source: https://github.com/MetaMask/metamask-extension/commit/a9d36860bec424dcee8db043d3e7da6a5ff5672e - it('should receive a payload modified with a prefix when Metamask is SignerType', async () => { - const orderHash = '0x34decbedc118904df65f379a175bb39ca18209d6ce41d5ed549d54e6e0a95004'; - const orderHashPrefixed = '0xae70f31d26096291aa681b26cb7574563956221d0b4213631e1ef9df675d4cba'; + }); + describe('#ecSignTypedDataOrderAsync', () => { + it('should result in the same signature as signing the order hash without an ethereum message prefix', async () => { + // Note: Since order hash is an EIP712 hash the result of a valid EIP712 signature + // of order hash is the same as signing the order without the Ethereum Message prefix. + const orderHashHex = orderHashUtils.getOrderHashHex(order); + const sig = ethUtil.ecsign( + ethUtil.toBuffer(orderHashHex), + Buffer.from('F2F48EE19680706196E2E339E5DA3491186E0C4C5030670656B0E0164837257D', 'hex'), + ); + const signatureBuffer = Buffer.concat([ + ethUtil.toBuffer(sig.v), + ethUtil.toBuffer(sig.r), + ethUtil.toBuffer(sig.s), + ethUtil.toBuffer(SignatureType.EIP712), + ]); + const signatureHex = `0x${signatureBuffer.toString('hex')}`; + const signedOrder = await signatureUtils.ecSignTypedDataOrderAsync(provider, order, makerAddress); + const isValidSignature = await signatureUtils.isValidSignatureAsync( + provider, + orderHashHex, + signedOrder.signature, + makerAddress, + ); + expect(signatureHex).to.eq(signedOrder.signature); + expect(isValidSignature).to.eq(true); + }); + it('should return the correct Signature for signatureHex concatenated as R + S + V', async () => { const expectedSignature = - '0x1b117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d872871137feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b03'; - // Generated from a MM eth_sign request from 0x5409ed021d9299bf6814279a6a1411a7e866a631 signing 0xae70f31d26096291aa681b26cb7574563956221d0b4213631e1ef9df675d4cba - const metamaskSignature = - '0x117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d872871137feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b1b'; + '0x1cd472c439833774b55d248c31b6585f21aea1b9363ebb4ec58549e46b62eb5a6f696f5781f62de008ee7f77650ef940d99c97ec1dee67b3f5cea1bbfdfeb2eba602'; const fakeProvider = { async sendAsync(payload: JSONRPCRequestPayload, callback: JSONRPCErrorCallback): Promise<void> { - if (payload.method === 'eth_sign') { - const [, message] = payload.params; - expect(message).to.equal(orderHashPrefixed); + if (payload.method === 'eth_signTypedData') { + const [address, typedData] = payload.params; + const signature = await web3Wrapper.signTypedDataAsync(address, typedData); callback(null, { id: 42, jsonrpc: '2.0', - result: metamaskSignature, + result: signature, }); } else { callback(null, { id: 42, jsonrpc: '2.0', result: [makerAddress] }); } }, }; - - const ecSignature = await signatureUtils.ecSignOrderHashAsync( - fakeProvider, - orderHash, - makerAddress, - SignerType.Metamask, - ); - expect(ecSignature).to.equal(expectedSignature); - }); - it('should return a valid signature', async () => { - const orderHash = '0x34decbedc118904df65f379a175bb39ca18209d6ce41d5ed549d54e6e0a95004'; - const ecSignature = await signatureUtils.ecSignOrderHashAsync( - provider, - orderHash, - makerAddress, - SignerType.Default, - ); - - const isValidSignature = await signatureUtils.isValidSignatureAsync( - provider, - orderHash, - ecSignature, - makerAddress, - ); - expect(isValidSignature).to.be.true(); + const signedOrder = await signatureUtils.ecSignTypedDataOrderAsync(fakeProvider, order, makerAddress); + expect(signedOrder.signature).to.equal(expectedSignature); }); }); describe('#convertECSignatureToSignatureHex', () => { @@ -256,38 +314,11 @@ describe('Signature utils', () => { r: '0xaca7da997ad177f040240cdccf6905b71ab16b74434388c3a72f34fd25d64393', s: '0x46b2bac274ff29b48b3ea6e2d04c1336eaceafda3c53ab483fc3ff12fac3ebf2', }; - it('should concatenate v,r,s and append the EthSign signature type when SignerType is Default', async () => { + it('should concatenate v,r,s and append the EthSign signature type', async () => { const expectedSignatureWithSignatureType = '0x1baca7da997ad177f040240cdccf6905b71ab16b74434388c3a72f34fd25d6439346b2bac274ff29b48b3ea6e2d04c1336eaceafda3c53ab483fc3ff12fac3ebf203'; - const signatureWithSignatureType = signatureUtils.convertECSignatureToSignatureHex( - ecSignature, - SignerType.Default, - ); + const signatureWithSignatureType = signatureUtils.convertECSignatureToSignatureHex(ecSignature); expect(signatureWithSignatureType).to.equal(expectedSignatureWithSignatureType); }); - it('should concatenate v,r,s and append the EthSign signature type when SignerType is Ledger', async () => { - const expectedSignatureWithSignatureType = - '0x1baca7da997ad177f040240cdccf6905b71ab16b74434388c3a72f34fd25d6439346b2bac274ff29b48b3ea6e2d04c1336eaceafda3c53ab483fc3ff12fac3ebf203'; - const signatureWithSignatureType = signatureUtils.convertECSignatureToSignatureHex( - ecSignature, - SignerType.Ledger, - ); - expect(signatureWithSignatureType).to.equal(expectedSignatureWithSignatureType); - }); - it('should concatenate v,r,s and append the EthSign signature type when SignerType is Metamask', async () => { - const expectedSignatureWithSignatureType = - '0x1baca7da997ad177f040240cdccf6905b71ab16b74434388c3a72f34fd25d6439346b2bac274ff29b48b3ea6e2d04c1336eaceafda3c53ab483fc3ff12fac3ebf203'; - const signatureWithSignatureType = signatureUtils.convertECSignatureToSignatureHex( - ecSignature, - SignerType.Metamask, - ); - expect(signatureWithSignatureType).to.equal(expectedSignatureWithSignatureType); - }); - it('should throw if the SignerType is invalid', async () => { - const expectedMessage = 'Unrecognized SignerType: INVALID_SIGNER'; - expect(() => - signatureUtils.convertECSignatureToSignatureHex(ecSignature, 'INVALID_SIGNER' as SignerType), - ).to.throw(expectedMessage); - }); }); }); diff --git a/packages/order-watcher/package.json b/packages/order-watcher/package.json index 1b075b8ea..9bdaef5b1 100644 --- a/packages/order-watcher/package.json +++ b/packages/order-watcher/package.json @@ -82,7 +82,7 @@ "bintrees": "^1.0.2", "ethereum-types": "^1.0.11", "ethereumjs-blockstream": "6.0.0", - "ethers": "4.0.0-beta.14", + "ethers": "~4.0.4", "lodash": "^4.17.5" }, "publishConfig": { diff --git a/packages/order-watcher/src/index.ts b/packages/order-watcher/src/index.ts index d7ad4fba7..d2f91eab1 100644 --- a/packages/order-watcher/src/index.ts +++ b/packages/order-watcher/src/index.ts @@ -12,4 +12,10 @@ export { export { OnOrderStateChangeCallback, OrderWatcherConfig } from './types'; export { SignedOrder } from '@0xproject/types'; -export { JSONRPCRequestPayload, JSONRPCErrorCallback, Provider, JSONRPCResponsePayload } from 'ethereum-types'; +export { + JSONRPCRequestPayload, + JSONRPCErrorCallback, + Provider, + JSONRPCResponsePayload, + JSONRPCResponseError, +} from 'ethereum-types'; diff --git a/packages/sol-cov/src/index.ts b/packages/sol-cov/src/index.ts index 15be86a9d..612d0869a 100644 --- a/packages/sol-cov/src/index.ts +++ b/packages/sol-cov/src/index.ts @@ -8,7 +8,13 @@ export { ProfilerSubprovider } from './profiler_subprovider'; export { RevertTraceSubprovider } from './revert_trace_subprovider'; export { ContractData } from './types'; -export { JSONRPCRequestPayload, Provider, JSONRPCErrorCallback, JSONRPCResponsePayload } from 'ethereum-types'; +export { + JSONRPCRequestPayload, + Provider, + JSONRPCErrorCallback, + JSONRPCResponsePayload, + JSONRPCResponseError, +} from 'ethereum-types'; export { JSONRPCRequestPayloadWithMethod, diff --git a/packages/subproviders/CHANGELOG.json b/packages/subproviders/CHANGELOG.json index 97f886f64..387207b01 100644 --- a/packages/subproviders/CHANGELOG.json +++ b/packages/subproviders/CHANGELOG.json @@ -1,5 +1,18 @@ [ { + "version": "2.1.0", + "changes": [ + { + "note": "Add `MetamaskSubprovider` to handle inconsistent JSON RPC behaviour", + "pr": 1102 + }, + { + "note": "Add support for `eth_signTypedData` in wallets Mnemonic, Private and EthLightWallet", + "pr": 1102 + } + ] + }, + { "version": "2.0.7", "changes": [ { diff --git a/packages/subproviders/package.json b/packages/subproviders/package.json index fad478349..ff3ab7ed5 100644 --- a/packages/subproviders/package.json +++ b/packages/subproviders/package.json @@ -45,7 +45,7 @@ "ethereum-types": "^1.0.11", "ethereumjs-tx": "^1.3.5", "ethereumjs-util": "^5.1.1", - "ganache-core": "0xProject/ganache-core#monorepo-dep", + "ganache-core": "^2.2.1", "hdkey": "^0.7.1", "json-rpc-error": "2.0.0", "lodash": "^4.17.5", diff --git a/packages/subproviders/src/index.ts b/packages/subproviders/src/index.ts index b5f9b3f90..9f4dac58b 100644 --- a/packages/subproviders/src/index.ts +++ b/packages/subproviders/src/index.ts @@ -27,6 +27,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 { MetamaskSubprovider } from './subproviders/metamask_subprovider'; export { EthLightwalletSubprovider } from './subproviders/eth_lightwallet_subprovider'; export { @@ -47,6 +48,19 @@ export { LedgerGetAddressResult, } from './types'; -export { ECSignature } from '@0xproject/types'; +export { + ECSignature, + EIP712Object, + EIP712ObjectValue, + EIP712TypedData, + EIP712Types, + EIP712Parameter, +} from '@0xproject/types'; -export { JSONRPCRequestPayload, Provider, JSONRPCResponsePayload, JSONRPCErrorCallback } from 'ethereum-types'; +export { + JSONRPCRequestPayload, + Provider, + JSONRPCResponsePayload, + JSONRPCErrorCallback, + JSONRPCResponseError, +} from 'ethereum-types'; diff --git a/packages/subproviders/src/subproviders/base_wallet_subprovider.ts b/packages/subproviders/src/subproviders/base_wallet_subprovider.ts index 4342e47e9..409a0d330 100644 --- a/packages/subproviders/src/subproviders/base_wallet_subprovider.ts +++ b/packages/subproviders/src/subproviders/base_wallet_subprovider.ts @@ -23,6 +23,7 @@ export abstract class BaseWalletSubprovider extends Subprovider { public abstract async getAccountsAsync(): Promise<string[]>; public abstract async signTransactionAsync(txParams: PartialTxParams): Promise<string>; public abstract async signPersonalMessageAsync(data: string, address: string): Promise<string>; + public abstract async signTypedDataAsync(address: string, typedData: any): Promise<string>; /** * This method conforms to the web3-provider-engine interface. @@ -36,6 +37,8 @@ export abstract class BaseWalletSubprovider extends Subprovider { public async handleRequest(payload: JSONRPCRequestPayload, next: Callback, end: ErrorCallback): Promise<void> { let accounts; let txParams; + let address; + let typedData; switch (payload.method) { case 'eth_coinbase': try { @@ -86,7 +89,7 @@ export abstract class BaseWalletSubprovider extends Subprovider { case 'eth_sign': case 'personal_sign': const data = payload.method === 'eth_sign' ? payload.params[1] : payload.params[0]; - const address = payload.method === 'eth_sign' ? payload.params[0] : payload.params[1]; + address = payload.method === 'eth_sign' ? payload.params[0] : payload.params[1]; try { const ecSignatureHex = await this.signPersonalMessageAsync(data, address); end(null, ecSignatureHex); @@ -94,6 +97,15 @@ export abstract class BaseWalletSubprovider extends Subprovider { end(err); } return; + case 'eth_signTypedData': + [address, typedData] = payload.params; + try { + const signature = await this.signTypedDataAsync(address, typedData); + end(null, signature); + } catch (err) { + end(err); + } + return; default: next(); diff --git a/packages/subproviders/src/subproviders/eth_lightwallet_subprovider.ts b/packages/subproviders/src/subproviders/eth_lightwallet_subprovider.ts index 6afd71422..a1d93ac49 100644 --- a/packages/subproviders/src/subproviders/eth_lightwallet_subprovider.ts +++ b/packages/subproviders/src/subproviders/eth_lightwallet_subprovider.ts @@ -1,3 +1,4 @@ +import { EIP712TypedData } from '@0xproject/types'; import * as lightwallet from 'eth-lightwallet'; import { PartialTxParams } from '../types'; @@ -48,16 +49,16 @@ export class EthLightwalletSubprovider extends BaseWalletSubprovider { // 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; + let privateKey = this._keystore.exportPrivateKey(txParams.from, this._pwDerivedKey); + const privateKeyWallet = new PrivateKeyWalletSubprovider(privateKey); + privateKey = ''; + const privateKeySignature = await privateKeyWallet.signTransactionAsync(txParams); + return privateKeySignature; } /** * 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` + * If you've added this Subprovider 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 @@ -65,10 +66,26 @@ export class EthLightwalletSubprovider extends BaseWalletSubprovider { * @return Signature hex string (order: rsv) */ public async signPersonalMessageAsync(data: string, address: string): Promise<string> { - let privKey = this._keystore.exportPrivateKey(address, this._pwDerivedKey); - const privKeyWallet = new PrivateKeyWalletSubprovider(privKey); - privKey = ''; - const result = privKeyWallet.signPersonalMessageAsync(data, address); + let privateKey = this._keystore.exportPrivateKey(address, this._pwDerivedKey); + const privateKeyWallet = new PrivateKeyWalletSubprovider(privateKey); + privateKey = ''; + const result = privateKeyWallet.signPersonalMessageAsync(data, address); + return result; + } + /** + * Sign an EIP712 Typed Data message. The signing address will associated with the provided address. + * If you've added this Subprovider to your app's provider, you can simply send an `eth_signTypedData` + * 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 address Address of the account to sign with + * @param data the typed data object + * @return Signature hex string (order: rsv) + */ + public async signTypedDataAsync(address: string, typedData: EIP712TypedData): Promise<string> { + let privateKey = this._keystore.exportPrivateKey(address, this._pwDerivedKey); + const privateKeyWallet = new PrivateKeyWalletSubprovider(privateKey); + privateKey = ''; + const result = privateKeyWallet.signTypedDataAsync(address, typedData); return result; } } diff --git a/packages/subproviders/src/subproviders/ledger.ts b/packages/subproviders/src/subproviders/ledger.ts index 6ad5de2e2..ee8edde92 100644 --- a/packages/subproviders/src/subproviders/ledger.ts +++ b/packages/subproviders/src/subproviders/ledger.ts @@ -187,6 +187,16 @@ export class LedgerSubprovider extends BaseWalletSubprovider { throw err; } } + /** + * eth_signTypedData is currently not supported on Ledger devices. + * @param address Address of the account to sign with + * @param data the typed data object + * @return Signature hex string (order: rsv) + */ + // tslint:disable-next-line:prefer-function-over-method + public async signTypedDataAsync(address: string, typedData: any): Promise<string> { + throw new Error(WalletSubproviderErrors.MethodNotSupported); + } private async _createLedgerClientAsync(): Promise<LedgerEthereumClient> { await this._connectionLock.acquire(); if (!_.isUndefined(this._ledgerClientIfExists)) { diff --git a/packages/subproviders/src/subproviders/metamask_subprovider.ts b/packages/subproviders/src/subproviders/metamask_subprovider.ts new file mode 100644 index 000000000..46fc2a9cd --- /dev/null +++ b/packages/subproviders/src/subproviders/metamask_subprovider.ts @@ -0,0 +1,126 @@ +import { marshaller, Web3Wrapper } from '@0xproject/web3-wrapper'; +import { JSONRPCRequestPayload, Provider } from 'ethereum-types'; +import * as ethUtil from 'ethereumjs-util'; + +import { Callback, ErrorCallback } from '../types'; + +import { Subprovider } from './subprovider'; + +/** + * This class implements the [web3-provider-engine](https://github.com/MetaMask/provider-engine) + * subprovider interface and the provider sendAsync interface. + * It handles inconsistencies with Metamask implementations of various JSON RPC methods. + * 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 MetamaskSubprovider extends Subprovider { + private readonly _web3Wrapper: Web3Wrapper; + private readonly _provider: Provider; + /** + * Instantiates a new MetamaskSubprovider + * @param provider Web3 provider that should handle all user account related requests + */ + constructor(provider: Provider) { + super(); + this._web3Wrapper = new Web3Wrapper(provider); + this._provider = provider; + } + /** + * This method conforms to the web3-provider-engine interface. + * It is called internally by the ProviderEngine when it is this subproviders + * turn to handle a JSON RPC request. + * @param payload JSON RPC payload + * @param next Callback to call if this subprovider decides not to handle the request + * @param end Callback to call if subprovider handled the request and wants to pass back the request. + */ + // tslint:disable-next-line:prefer-function-over-method async-suffix + public async handleRequest(payload: JSONRPCRequestPayload, next: Callback, end: ErrorCallback): Promise<void> { + let message; + let address; + switch (payload.method) { + case 'web3_clientVersion': + try { + const nodeVersion = await this._web3Wrapper.getNodeVersionAsync(); + end(null, nodeVersion); + } catch (err) { + end(err); + } + return; + case 'eth_accounts': + try { + const accounts = await this._web3Wrapper.getAvailableAddressesAsync(); + end(null, accounts); + } catch (err) { + end(err); + } + return; + case 'eth_sendTransaction': + const [txParams] = payload.params; + try { + const txData = marshaller.unmarshalTxData(txParams); + const txHash = await this._web3Wrapper.sendTransactionAsync(txData); + end(null, txHash); + } catch (err) { + end(err); + } + return; + case 'eth_sign': + [address, message] = payload.params; + try { + // Metamask incorrectly implements eth_sign and does not prefix the message as per the spec + // Source: https://github.com/MetaMask/metamask-extension/commit/a9d36860bec424dcee8db043d3e7da6a5ff5672e + const msgBuff = ethUtil.toBuffer(message); + const prefixedMsgBuff = ethUtil.hashPersonalMessage(msgBuff); + const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff); + const signature = await this._web3Wrapper.signMessageAsync(address, prefixedMsgHex); + signature ? end(null, signature) : end(new Error('Error performing eth_sign'), null); + } catch (err) { + end(err); + } + return; + case 'eth_signTypedData': + case 'eth_signTypedData_v3': + [address, message] = payload.params; + try { + // Metamask supports multiple versions and has namespaced signTypedData to v3 for an indeterminate period of time. + // eth_signTypedData is mapped to an older implementation before the spec was finalized. + // Source: https://github.com/MetaMask/metamask-extension/blob/c49d854b55b3efd34c7fd0414b76f7feaa2eec7c/app/scripts/metamask-controller.js#L1262 + // and expects message to be serialised as JSON + const messageJSON = JSON.stringify(message); + const signature = await this._web3Wrapper.sendRawPayloadAsync<string>({ + method: 'eth_signTypedData_v3', + params: [address, messageJSON], + }); + signature ? end(null, signature) : end(new Error('Error performing eth_signTypedData'), null); + } catch (err) { + end(err); + } + return; + default: + next(); + return; + } + } + /** + * This method conforms to the provider sendAsync interface. + * Allowing the MetamaskSubprovider to be used as a generic provider (outside of Web3ProviderEngine) with the + * addition of wrapping the inconsistent Metamask behaviour + * @param payload JSON RPC payload + * @return The contents nested under the result key of the response body + */ + public sendAsync(payload: JSONRPCRequestPayload, callback: ErrorCallback): void { + void this.handleRequest( + payload, + // handleRequest has decided to not handle this, so fall through to the provider + () => { + const sendAsync = this._provider.sendAsync.bind(this._provider); + sendAsync(payload, callback); + }, + // handleRequest has called end and will handle this + (err, data) => { + err ? callback(err) : callback(null, { ...payload, result: data }); + }, + ); + } +} diff --git a/packages/subproviders/src/subproviders/mnemonic_wallet.ts b/packages/subproviders/src/subproviders/mnemonic_wallet.ts index 1495112b6..04a11c7be 100644 --- a/packages/subproviders/src/subproviders/mnemonic_wallet.ts +++ b/packages/subproviders/src/subproviders/mnemonic_wallet.ts @@ -1,4 +1,5 @@ import { assert } from '@0xproject/assert'; +import { EIP712TypedData } from '@0xproject/types'; import { addressUtils } from '@0xproject/utils'; import * as bip39 from 'bip39'; import HDNode = require('hdkey'); @@ -90,10 +91,10 @@ export class MnemonicWalletSubprovider extends BaseWalletSubprovider { } /** * 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. + * 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) @@ -108,6 +109,25 @@ export class MnemonicWalletSubprovider extends BaseWalletSubprovider { const sig = await privateKeyWallet.signPersonalMessageAsync(data, address); return sig; } + /** + * Sign an EIP712 Typed Data message. The signing account will be the account + * associated with the provided address. If you've added this MnemonicWalletSubprovider to + * your app's provider, you can simply send an `eth_signTypedData` 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 address Address of the account to sign with + * @param data the typed data object + * @return Signature hex string (order: rsv) + */ + public async signTypedDataAsync(address: string, typedData: EIP712TypedData): Promise<string> { + if (_.isUndefined(typedData)) { + throw new Error(WalletSubproviderErrors.DataMissingForSignPersonalMessage); + } + assert.isETHAddressHex('address', address); + const privateKeyWallet = this._privateKeyWalletForAddress(address); + const sig = await privateKeyWallet.signTypedDataAsync(address, typedData); + return sig; + } private _privateKeyWalletForAddress(address: string): PrivateKeyWalletSubprovider { const derivedKeyInfo = this._findDerivedKeyInfoForAddress(address); const privateKeyHex = derivedKeyInfo.hdKey.privateKey.toString('hex'); diff --git a/packages/subproviders/src/subproviders/private_key_wallet.ts b/packages/subproviders/src/subproviders/private_key_wallet.ts index 9d6fc487e..e89c4c186 100644 --- a/packages/subproviders/src/subproviders/private_key_wallet.ts +++ b/packages/subproviders/src/subproviders/private_key_wallet.ts @@ -1,4 +1,6 @@ import { assert } from '@0xproject/assert'; +import { EIP712TypedData } from '@0xproject/types'; +import { signTypedDataUtils } from '@0xproject/utils'; import EthereumTx = require('ethereumjs-tx'); import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; @@ -23,7 +25,7 @@ export class PrivateKeyWalletSubprovider extends BaseWalletSubprovider { constructor(privateKey: string) { assert.isString('privateKey', privateKey); super(); - this._privateKeyBuffer = new Buffer(privateKey, 'hex'); + this._privateKeyBuffer = Buffer.from(privateKey, 'hex'); this._address = `0x${ethUtil.privateToAddress(this._privateKeyBuffer).toString('hex')}`; } /** @@ -84,4 +86,29 @@ export class PrivateKeyWalletSubprovider extends BaseWalletSubprovider { const rpcSig = ethUtil.toRpcSig(sig.v, sig.r, sig.s); return rpcSig; } + /** + * Sign an EIP712 Typed Data message. The signing address will be calculated from the private key. + * The address must be provided it must match the address calculated from the private key. + * If you've added this Subprovider to your app's provider, you can simply send an `eth_signTypedData` + * 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 address Address of the account to sign with + * @param data the typed data object + * @return Signature hex string (order: rsv) + */ + public async signTypedDataAsync(address: string, typedData: EIP712TypedData): Promise<string> { + if (_.isUndefined(typedData)) { + throw new Error(WalletSubproviderErrors.DataMissingForSignTypedData); + } + assert.isETHAddressHex('address', address); + if (address !== this._address) { + throw new Error( + `Requested to sign message with address: ${address}, instantiated with address: ${this._address}`, + ); + } + const dataBuff = signTypedDataUtils.generateTypedDataHash(typedData); + const sig = ethUtil.ecsign(dataBuff, this._privateKeyBuffer); + const rpcSig = ethUtil.toRpcSig(sig.v, sig.r, sig.s); + return rpcSig; + } } diff --git a/packages/subproviders/src/subproviders/signer.ts b/packages/subproviders/src/subproviders/signer.ts index d5fd86897..eda7db42e 100644 --- a/packages/subproviders/src/subproviders/signer.ts +++ b/packages/subproviders/src/subproviders/signer.ts @@ -14,7 +14,7 @@ import { Subprovider } from './subprovider'; export class SignerSubprovider extends Subprovider { private readonly _web3Wrapper: Web3Wrapper; /** - * Instantiates a new SignerSubprovider + * Instantiates a new SignerSubprovider. * @param provider Web3 provider that should handle all user account related requests */ constructor(provider: Provider) { @@ -31,6 +31,8 @@ export class SignerSubprovider extends Subprovider { */ // tslint:disable-next-line:prefer-function-over-method async-suffix public async handleRequest(payload: JSONRPCRequestPayload, next: Callback, end: ErrorCallback): Promise<void> { + let message; + let address; switch (payload.method) { case 'web3_clientVersion': try { @@ -59,7 +61,7 @@ export class SignerSubprovider extends Subprovider { } return; case 'eth_sign': - const [address, message] = payload.params; + [address, message] = payload.params; try { const signature = await this._web3Wrapper.signMessageAsync(address, message); end(null, signature); @@ -67,6 +69,15 @@ export class SignerSubprovider extends Subprovider { end(err); } return; + case 'eth_signTypedData': + [address, message] = payload.params; + try { + const signature = await this._web3Wrapper.signTypedDataAsync(address, message); + end(null, signature); + } catch (err) { + end(err); + } + return; default: next(); return; diff --git a/packages/subproviders/src/types.ts b/packages/subproviders/src/types.ts index fe58bffa5..e8a47ad34 100644 --- a/packages/subproviders/src/types.ts +++ b/packages/subproviders/src/types.ts @@ -107,8 +107,10 @@ export interface ResponseWithTxParams { export enum WalletSubproviderErrors { AddressNotFound = 'ADDRESS_NOT_FOUND', DataMissingForSignPersonalMessage = 'DATA_MISSING_FOR_SIGN_PERSONAL_MESSAGE', + DataMissingForSignTypedData = 'DATA_MISSING_FOR_SIGN_TYPED_DATA', SenderInvalidOrNotSupplied = 'SENDER_INVALID_OR_NOT_SUPPLIED', FromAddressMissingOrInvalid = 'FROM_ADDRESS_MISSING_OR_INVALID', + MethodNotSupported = 'METHOD_NOT_SUPPORTED', } export enum LedgerSubproviderErrors { TooOldLedgerFirmware = 'TOO_OLD_LEDGER_FIRMWARE', diff --git a/packages/subproviders/test/unit/eth_lightwallet_subprovider_test.ts b/packages/subproviders/test/unit/eth_lightwallet_subprovider_test.ts index 063817a95..49698ce9e 100644 --- a/packages/subproviders/test/unit/eth_lightwallet_subprovider_test.ts +++ b/packages/subproviders/test/unit/eth_lightwallet_subprovider_test.ts @@ -73,6 +73,13 @@ describe('EthLightwalletSubprovider', () => { const txHex = await ethLightwalletSubprovider.signTransactionAsync(fixtureData.TX_DATA); expect(txHex).to.be.equal(fixtureData.TX_DATA_SIGNED_RESULT); }); + it('signs an EIP712 sign typed data message', async () => { + const signature = await ethLightwalletSubprovider.signTypedDataAsync( + fixtureData.TEST_RPC_ACCOUNT_0, + fixtureData.EIP712_TEST_TYPED_DATA, + ); + expect(signature).to.be.equal(fixtureData.EIP712_TEST_TYPED_DATA_SIGNED_RESULT); + }); }); }); describe('calls through a provider', () => { @@ -129,6 +136,20 @@ describe('EthLightwalletSubprovider', () => { }); provider.sendAsync(payload, callback); }); + it('signs an EIP712 sign typed data message with eth_signTypedData', (done: DoneCallback) => { + const payload = { + jsonrpc: '2.0', + method: 'eth_signTypedData', + params: [fixtureData.TEST_RPC_ACCOUNT_0, fixtureData.EIP712_TEST_TYPED_DATA], + id: 1, + }; + const callback = reportCallbackErrors(done)((err: Error, response: JSONRPCResponsePayload) => { + expect(err).to.be.a('null'); + expect(response.result).to.be.equal(fixtureData.EIP712_TEST_TYPED_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) => { diff --git a/packages/subproviders/test/unit/mnemonic_wallet_subprovider_test.ts b/packages/subproviders/test/unit/mnemonic_wallet_subprovider_test.ts index f2bdda3cd..61dcbf6da 100644 --- a/packages/subproviders/test/unit/mnemonic_wallet_subprovider_test.ts +++ b/packages/subproviders/test/unit/mnemonic_wallet_subprovider_test.ts @@ -47,6 +47,13 @@ describe('MnemonicWalletSubprovider', () => { const txHex = await subprovider.signTransactionAsync(txData); expect(txHex).to.be.equal(fixtureData.TX_DATA_ACCOUNT_1_SIGNED_RESULT); }); + it('signs an EIP712 sign typed data message', async () => { + const signature = await subprovider.signTypedDataAsync( + fixtureData.TEST_RPC_ACCOUNT_0, + fixtureData.EIP712_TEST_TYPED_DATA, + ); + expect(signature).to.be.equal(fixtureData.EIP712_TEST_TYPED_DATA_SIGNED_RESULT); + }); }); describe('failure cases', () => { it('throws an error if address is invalid ', async () => { @@ -118,6 +125,20 @@ describe('MnemonicWalletSubprovider', () => { }); provider.sendAsync(payload, callback); }); + it('signs an EIP712 sign typed data message with eth_signTypedData', (done: DoneCallback) => { + const payload = { + jsonrpc: '2.0', + method: 'eth_signTypedData', + params: [fixtureData.TEST_RPC_ACCOUNT_0, fixtureData.EIP712_TEST_TYPED_DATA], + id: 1, + }; + const callback = reportCallbackErrors(done)((err: Error, response: JSONRPCResponsePayload) => { + expect(err).to.be.a('null'); + expect(response.result).to.be.equal(fixtureData.EIP712_TEST_TYPED_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) => { 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 95773145f..4cd70e5ed 100644 --- a/packages/subproviders/test/unit/private_key_wallet_subprovider_test.ts +++ b/packages/subproviders/test/unit/private_key_wallet_subprovider_test.ts @@ -32,6 +32,13 @@ describe('PrivateKeyWalletSubprovider', () => { const txHex = await subprovider.signTransactionAsync(fixtureData.TX_DATA); expect(txHex).to.be.equal(fixtureData.TX_DATA_SIGNED_RESULT); }); + it('signs an EIP712 sign typed data message', async () => { + const signature = await subprovider.signTypedDataAsync( + fixtureData.TEST_RPC_ACCOUNT_0, + fixtureData.EIP712_TEST_TYPED_DATA, + ); + expect(signature).to.be.equal(fixtureData.EIP712_TEST_TYPED_DATA_SIGNED_RESULT); + }); }); }); describe('calls through a provider', () => { @@ -103,6 +110,20 @@ describe('PrivateKeyWalletSubprovider', () => { }); provider.sendAsync(payload, callback); }); + it('signs an EIP712 sign typed data message with eth_signTypedData', (done: DoneCallback) => { + const payload = { + jsonrpc: '2.0', + method: 'eth_signTypedData', + params: [fixtureData.TEST_RPC_ACCOUNT_0, fixtureData.EIP712_TEST_TYPED_DATA], + id: 1, + }; + const callback = reportCallbackErrors(done)((err: Error, response: JSONRPCResponsePayload) => { + expect(err).to.be.a('null'); + expect(response.result).to.be.equal(fixtureData.EIP712_TEST_TYPED_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) => { diff --git a/packages/subproviders/test/utils/fixture_data.ts b/packages/subproviders/test/utils/fixture_data.ts index 7cf502c97..3eb4493b5 100644 --- a/packages/subproviders/test/utils/fixture_data.ts +++ b/packages/subproviders/test/utils/fixture_data.ts @@ -30,4 +30,35 @@ export const fixtureData = { '0xf85f8080822710940000000000000000000000000000000000000000808078a0712854c73c69445cc1b22a7c3d7312ff9a97fe4ffba35fd636e8236b211b6e7ca0647cee031615e52d916c7c707025bc64ad525d8f1b9876c3435a863b42743178', TX_DATA_ACCOUNT_1_SIGNED_RESULT: '0xf85f8080822710940000000000000000000000000000000000000000808078a04b02af7ff3f18ce114b601542cc8ebdc50921354f75dd510d31793453a0710e6a0540082a01e475465801b8186a2edc79ec1a2dcf169b9781c25a58a417023c9ca', + EIP712_TEST_TYPED_DATA: { + types: { + EIP712Domain: [ + { + name: 'name', + type: 'string', + }, + ], + Test: [ + { + name: 'testAddress', + type: 'address', + }, + { + name: 'testNumber', + type: 'uint256', + }, + ], + }, + domain: { + name: 'Test', + }, + message: { + testAddress: '0x0000000000000000000000000000000000000000', + testNumber: '12345', + }, + primaryType: 'Test', + }, + EIP712_TEST_TYPED_DATA_HASH: '0xb460d69ca60383293877cd765c0f97bd832d66bca720f7e32222ce1118832493', + EIP712_TEST_TYPED_DATA_SIGNED_RESULT: + '0x20af5b6bfc3658942198d6eeda159b4ed589f90cee6eac3ba117818ffba5fd7e354a353aad93faabd6eb6c66e17921c92bd1cd09c92a770f554470dc3e254ce701', }; diff --git a/packages/testnet-faucets/gulpfile.js b/packages/testnet-faucets/gulpfile.js index 7c4e25e0f..839ef851b 100644 --- a/packages/testnet-faucets/gulpfile.js +++ b/packages/testnet-faucets/gulpfile.js @@ -14,10 +14,7 @@ const config = { }, devtool: 'source-map', resolve: { - modules: [ - path.join(__dirname, '/src/ts'), - 'node_modules', - ], + modules: [path.join(__dirname, '/src/ts'), 'node_modules'], extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'], alias: { ts: path.join(__dirname, '/src/ts'), @@ -44,10 +41,10 @@ const config = { }), ], externals: nodeExternals({ - modulesDir: path.join(__dirname, '../../node_modules') + modulesDir: path.join(__dirname, '../../node_modules'), }), watchOptions: { - ignored: /server|node_modules|transpiled/ + ignored: /server|node_modules|transpiled/, }, }; @@ -77,16 +74,18 @@ gulp.task('run', ['watch'], function() { }); function onBuild(done) { - return function(err, stats) { - if(err) { - console.log('Error', err); - } - else { - console.log(stats.toString()); - } - - if(done) { - done(); - } - } + return function(err, stats) { + if (err) { + console.log('Error', err); + process.exit(1); + } else { + console.log(stats.toString()); + } + if (done) { + if (stats.compilation.errors && stats.compilation.errors.length > 0) { + process.exit(1); + } + done(); + } + }; } diff --git a/packages/testnet-faucets/src/ts/handler.ts b/packages/testnet-faucets/src/ts/handler.ts index e73eb1744..be42ebde8 100644 --- a/packages/testnet-faucets/src/ts/handler.ts +++ b/packages/testnet-faucets/src/ts/handler.ts @@ -9,7 +9,6 @@ import { RPCSubprovider, signatureUtils, SignedOrder, - SignerType, Web3ProviderEngine, } from '0x.js'; import { NonceTrackerSubprovider, PrivateKeyWalletSubprovider } from '@0xproject/subproviders'; @@ -180,11 +179,10 @@ export class Handler { expirationTimeSeconds: new BigNumber(Date.now() + FIVE_DAYS_IN_MS).div(1000).floor(), }; const orderHash = orderHashUtils.getOrderHashHex(order); - const signature = await signatureUtils.ecSignOrderHashAsync( + const signature = await signatureUtils.ecSignHashAsync( networkConfig.web3Wrapper.getProvider(), orderHash, configs.DISPENSER_ADDRESS, - SignerType.Default, ); const signedOrder: SignedOrder = { ...order, diff --git a/packages/types/CHANGELOG.json b/packages/types/CHANGELOG.json index 6bb6ced70..53e1f3716 100644 --- a/packages/types/CHANGELOG.json +++ b/packages/types/CHANGELOG.json @@ -1,5 +1,18 @@ [ { + "version": "1.2.0", + "changes": [ + { + "note": "Added `EIP712Parameter` `EIP712Types` `EIP712TypedData` for EIP712 signing", + "pr": 1102 + }, + { + "note": "Added `ZeroExTransaction` type for Exchange executeTransaction", + "pr": 1102 + } + ] + }, + { "timestamp": 1538693146, "version": "1.1.4", "changes": [ diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 3ae0536d5..6bc966ba1 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -42,6 +42,15 @@ export interface SignedOrder extends Order { } /** + * ZeroExTransaction for use with 0x Exchange executeTransaction + */ +export interface ZeroExTransaction { + salt: BigNumber; + signerAddress: string; + data: string; +} + +/** * Elliptic Curve signature */ export interface ECSignature { @@ -143,16 +152,6 @@ export enum SignatureType { NSignatureTypes, } -/** - * The type of the Signer implementation. Some signer implementations use different message prefixes or implement different - * eth_sign behaviour (e.g Metamask). Default assumes a spec compliant `eth_sign`. - */ -export enum SignerType { - Default = 'DEFAULT', - Ledger = 'LEDGER', - Metamask = 'METAMASK', -} - export enum AssetProxyId { ERC20 = '0xf47261b0', ERC721 = '0x02571792', @@ -599,3 +598,25 @@ export interface Metadata { externalTypeToLink: ExternalTypeToLink; externalExportToLink: ExternalExportToLink; } + +export interface EIP712Parameter { + name: string; + type: string; +} + +export interface EIP712Types { + [key: string]: EIP712Parameter[]; +} + +export type EIP712ObjectValue = string | number | EIP712Object; + +export interface EIP712Object { + [key: string]: EIP712ObjectValue; +} + +export interface EIP712TypedData { + types: EIP712Types; + domain: EIP712Object; + message: EIP712Object; + primaryType: string; +} diff --git a/packages/utils/package.json b/packages/utils/package.json index f1017f84d..20b0f0c86 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -50,7 +50,7 @@ "detect-node": "2.0.3", "ethereum-types": "^1.0.11", "ethereumjs-util": "^5.1.1", - "ethers": "4.0.0-beta.14", + "ethers": "~4.0.4", "isomorphic-fetch": "^2.2.1", "js-sha3": "^0.7.0", "lodash": "^4.17.5" diff --git a/packages/utils/src/abi_decoder.ts b/packages/utils/src/abi_decoder.ts index ea8c91d10..ac3e54efb 100644 --- a/packages/utils/src/abi_decoder.ts +++ b/packages/utils/src/abi_decoder.ts @@ -9,7 +9,7 @@ import { RawLog, SolidityTypes, } from 'ethereum-types'; -import * as ethers from 'ethers'; +import { ethers } from 'ethers'; import * as _ from 'lodash'; import { addressUtils } from './address_utils'; @@ -41,7 +41,7 @@ export class AbiDecoder { return log; } const event = this._methodIds[methodId][numIndexedArgs]; - const ethersInterface = new ethers.Interface([event]); + const ethersInterface = new ethers.utils.Interface([event]); const decodedParams: DecodedLogArgs = {}; let topicsIndex = 1; @@ -96,7 +96,7 @@ export class AbiDecoder { if (_.isUndefined(abiArray)) { return; } - const ethersInterface = new ethers.Interface(abiArray); + const ethersInterface = new ethers.utils.Interface(abiArray); _.map(abiArray, (abi: AbiDefinition) => { if (abi.type === AbiType.Event) { const topic = ethersInterface.events[abi.name].topic; diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 9d01e5bc5..0723e5788 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -9,3 +9,4 @@ export { abiUtils } from './abi_utils'; export { NULL_BYTES } from './constants'; export { errorUtils } from './error_utils'; export { fetchAsync } from './fetch_async'; +export { signTypedDataUtils } from './sign_typed_data_utils'; diff --git a/packages/utils/src/sign_typed_data_utils.ts b/packages/utils/src/sign_typed_data_utils.ts new file mode 100644 index 000000000..cd5bcb42f --- /dev/null +++ b/packages/utils/src/sign_typed_data_utils.ts @@ -0,0 +1,82 @@ +import * as ethUtil from 'ethereumjs-util'; +import * as ethers from 'ethers'; +import * as _ from 'lodash'; + +import { EIP712Object, EIP712ObjectValue, EIP712TypedData, EIP712Types } from '@0xproject/types'; + +export const signTypedDataUtils = { + /** + * Generates the EIP712 Typed Data hash for signing + * @param typedData An object that conforms to the EIP712TypedData interface + * @return A Buffer containing the hash of the typed data. + */ + generateTypedDataHash(typedData: EIP712TypedData): Buffer { + return ethUtil.sha3( + Buffer.concat([ + Buffer.from('1901', 'hex'), + signTypedDataUtils._structHash('EIP712Domain', typedData.domain, typedData.types), + signTypedDataUtils._structHash(typedData.primaryType, typedData.message, typedData.types), + ]), + ); + }, + _findDependencies(primaryType: string, types: EIP712Types, found: string[] = []): string[] { + if (found.includes(primaryType) || types[primaryType] === undefined) { + return found; + } + found.push(primaryType); + for (const field of types[primaryType]) { + for (const dep of signTypedDataUtils._findDependencies(field.type, types, found)) { + if (!found.includes(dep)) { + found.push(dep); + } + } + } + return found; + }, + _encodeType(primaryType: string, types: EIP712Types): string { + let deps = signTypedDataUtils._findDependencies(primaryType, types); + deps = deps.filter(d => d !== primaryType); + deps = [primaryType].concat(deps.sort()); + let result = ''; + for (const dep of deps) { + result += `${dep}(${types[dep].map(({ name, type }) => `${type} ${name}`).join(',')})`; + } + return result; + }, + _encodeData(primaryType: string, data: EIP712Object, types: EIP712Types): string { + const encodedTypes = ['bytes32']; + const encodedValues: Array<Buffer | EIP712ObjectValue> = [signTypedDataUtils._typeHash(primaryType, types)]; + for (const field of types[primaryType]) { + const value = data[field.name]; + if (field.type === 'string' || field.type === 'bytes') { + const hashValue = ethUtil.sha3(value as string); + encodedTypes.push('bytes32'); + encodedValues.push(hashValue); + } else if (types[field.type] !== undefined) { + encodedTypes.push('bytes32'); + const hashValue = ethUtil.sha3( + // tslint:disable-next-line:no-unnecessary-type-assertion + signTypedDataUtils._encodeData(field.type, value as EIP712Object, types), + ); + encodedValues.push(hashValue); + } else if (field.type.lastIndexOf(']') === field.type.length - 1) { + throw new Error('Arrays currently unimplemented in encodeData'); + } else { + encodedTypes.push(field.type); + const normalizedValue = signTypedDataUtils._normalizeValue(field.type, value); + encodedValues.push(normalizedValue); + } + } + return ethers.utils.defaultAbiCoder.encode(encodedTypes, encodedValues); + }, + _normalizeValue(type: string, value: any): EIP712ObjectValue { + const normalizedValue = type === 'uint256' && _.isObject(value) && value.isBigNumber ? value.toString() : value; + return normalizedValue; + }, + _typeHash(primaryType: string, types: EIP712Types): Buffer { + return ethUtil.sha3(signTypedDataUtils._encodeType(primaryType, types)); + }, + _structHash(primaryType: string, data: EIP712Object, types: EIP712Types): Buffer { + return ethUtil.sha3(signTypedDataUtils._encodeData(primaryType, data, types)); + }, +}; diff --git a/packages/utils/test/sign_typed_data_utils_test.ts b/packages/utils/test/sign_typed_data_utils_test.ts new file mode 100644 index 000000000..dcba08b04 --- /dev/null +++ b/packages/utils/test/sign_typed_data_utils_test.ts @@ -0,0 +1,140 @@ +import * as chai from 'chai'; +import 'mocha'; + +import { signTypedDataUtils } from '../src/sign_typed_data_utils'; + +const expect = chai.expect; + +describe('signTypedDataUtils', () => { + describe('signTypedDataHash', () => { + const simpleSignTypedDataHashHex = '0xb460d69ca60383293877cd765c0f97bd832d66bca720f7e32222ce1118832493'; + const simpleSignTypedData = { + types: { + EIP712Domain: [ + { + name: 'name', + type: 'string', + }, + ], + Test: [ + { + name: 'testAddress', + type: 'address', + }, + { + name: 'testNumber', + type: 'uint256', + }, + ], + }, + domain: { + name: 'Test', + }, + message: { + testAddress: '0x0000000000000000000000000000000000000000', + testNumber: '12345', + }, + primaryType: 'Test', + }; + const orderSignTypedDataHashHex = '0x55eaa6ec02f3224d30873577e9ddd069a288c16d6fb407210eecbc501fa76692'; + const orderSignTypedData = { + types: { + EIP712Domain: [ + { + name: 'name', + type: 'string', + }, + { + name: 'version', + type: 'string', + }, + { + name: 'verifyingContract', + type: 'address', + }, + ], + Order: [ + { + name: 'makerAddress', + type: 'address', + }, + { + name: 'takerAddress', + type: 'address', + }, + { + name: 'feeRecipientAddress', + type: 'address', + }, + { + name: 'senderAddress', + type: 'address', + }, + { + name: 'makerAssetAmount', + type: 'uint256', + }, + { + name: 'takerAssetAmount', + type: 'uint256', + }, + { + name: 'makerFee', + type: 'uint256', + }, + { + name: 'takerFee', + type: 'uint256', + }, + { + name: 'expirationTimeSeconds', + type: 'uint256', + }, + { + name: 'salt', + type: 'uint256', + }, + { + name: 'makerAssetData', + type: 'bytes', + }, + { + name: 'takerAssetData', + type: 'bytes', + }, + ], + }, + domain: { + name: '0x Protocol', + version: '2', + verifyingContract: '0x0000000000000000000000000000000000000000', + }, + message: { + makerAddress: '0x0000000000000000000000000000000000000000', + takerAddress: '0x0000000000000000000000000000000000000000', + makerAssetAmount: '1000000000000000000', + takerAssetAmount: '1000000000000000000', + expirationTimeSeconds: '12345', + makerFee: '0', + takerFee: '0', + feeRecipientAddress: '0x0000000000000000000000000000000000000000', + senderAddress: '0x0000000000000000000000000000000000000000', + salt: '12345', + makerAssetData: '0x0000000000000000000000000000000000000000', + takerAssetData: '0x0000000000000000000000000000000000000000', + exchangeAddress: '0x0000000000000000000000000000000000000000', + }, + primaryType: 'Order', + }; + it('creates a hash of the test sign typed data', () => { + const hash = signTypedDataUtils.generateTypedDataHash(simpleSignTypedData).toString('hex'); + const hashHex = `0x${hash}`; + expect(hashHex).to.be.eq(simpleSignTypedDataHashHex); + }); + it('creates a hash of the order sign typed data', () => { + const hash = signTypedDataUtils.generateTypedDataHash(orderSignTypedData).toString('hex'); + const hashHex = `0x${hash}`; + expect(hashHex).to.be.eq(orderSignTypedDataHashHex); + }); + }); +}); diff --git a/packages/web3-wrapper/CHANGELOG.json b/packages/web3-wrapper/CHANGELOG.json index 47f054300..be5c1fef6 100644 --- a/packages/web3-wrapper/CHANGELOG.json +++ b/packages/web3-wrapper/CHANGELOG.json @@ -1,5 +1,19 @@ [ { + "version": "3.1.0", + "changes": [ + { + "note": "Add `signTypedData` to perform EIP712 `eth_signTypedData`.", + "pr": 1102 + }, + { + "note": + "Web3Wrapper now throws when an RPC request contains an error field in the response. Previously errors could be swallowed and undefined returned.", + "pr": 1102 + } + ] + }, + { "version": "3.0.3", "changes": [ { diff --git a/packages/web3-wrapper/package.json b/packages/web3-wrapper/package.json index ef31a68dc..a3f82c87e 100644 --- a/packages/web3-wrapper/package.json +++ b/packages/web3-wrapper/package.json @@ -60,7 +60,7 @@ "@0xproject/utils": "^2.0.2", "ethereum-types": "^1.0.11", "ethereumjs-util": "^5.1.1", - "ethers": "4.0.0-beta.14", + "ethers": "~4.0.4", "lodash": "^4.17.5" }, "publishConfig": { diff --git a/packages/web3-wrapper/src/index.ts b/packages/web3-wrapper/src/index.ts index 7cdd25e55..9bef06fd4 100644 --- a/packages/web3-wrapper/src/index.ts +++ b/packages/web3-wrapper/src/index.ts @@ -30,6 +30,7 @@ export { OpCode, TxDataPayable, JSONRPCResponsePayload, + JSONRPCResponseError, RawLogEntry, DecodedLogEntryEvent, LogWithDecodedArgs, diff --git a/packages/web3-wrapper/src/web3_wrapper.ts b/packages/web3-wrapper/src/web3_wrapper.ts index d52c1cb6e..726246f1a 100644 --- a/packages/web3-wrapper/src/web3_wrapper.ts +++ b/packages/web3-wrapper/src/web3_wrapper.ts @@ -315,6 +315,21 @@ export class Web3Wrapper { return signData; } /** + * Sign an EIP712 typed data message with a specific address's private key (`eth_signTypedData`) + * @param address Address of signer + * @param typedData Typed data message to sign + * @returns Signature string (as RSV) + */ + public async signTypedDataAsync(address: string, typedData: any): Promise<string> { + assert.isETHAddressHex('address', address); + assert.doesConformToSchema('typedData', typedData, schemas.eip712TypedDataSchema); + const signData = await this.sendRawPayloadAsync<string>({ + method: 'eth_signTypedData', + params: [address, typedData], + }); + return signData; + } + /** * Fetches the latest block number * @returns Block number */ @@ -654,6 +669,9 @@ export class Web3Wrapper { ...payload, }; const response = await promisify<JSONRPCResponsePayload>(sendAsync)(payloadWithDefaults); + if (response.error) { + throw new Error(response.error.message); + } const result = response.result; return result; } diff --git a/packages/web3-wrapper/test/web3_wrapper_test.ts b/packages/web3-wrapper/test/web3_wrapper_test.ts index 385c469bf..164253777 100644 --- a/packages/web3-wrapper/test/web3_wrapper_test.ts +++ b/packages/web3-wrapper/test/web3_wrapper_test.ts @@ -1,5 +1,5 @@ import * as chai from 'chai'; -import { BlockParamLiteral } from 'ethereum-types'; +import { BlockParamLiteral, JSONRPCErrorCallback, JSONRPCRequestPayload } from 'ethereum-types'; import * as Ganache from 'ganache-core'; import * as _ from 'lodash'; import 'mocha'; @@ -78,6 +78,19 @@ describe('Web3Wrapper tests', () => { const signatureLength = 132; expect(signature.length).to.be.equal(signatureLength); }); + it('should throw if the provider returns an error', async () => { + const message = '0xdeadbeef'; + const signer = addresses[1]; + const fakeProvider = { + async sendAsync(payload: JSONRPCRequestPayload, callback: JSONRPCErrorCallback): Promise<void> { + callback(new Error('User denied message signature')); + }, + }; + const errorWeb3Wrapper = new Web3Wrapper(fakeProvider); + expect(errorWeb3Wrapper.signMessageAsync(signer, message)).to.be.rejectedWith( + 'User denied message signature', + ); + }); }); describe('#getBlockNumberAsync', () => { it('get block number', async () => { diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index c420bbf3a..b1181e4c6 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -9,11 +9,12 @@ import { ExchangeFillEventArgs, IndexedFilterValues, } from '@0xproject/contract-wrappers'; -import { assetDataUtils, orderHashUtils, signatureUtils, SignerType } from '@0xproject/order-utils'; +import { assetDataUtils, orderHashUtils, signatureUtils } from '@0xproject/order-utils'; import { EtherscanLinkSuffixes, utils as sharedUtils } from '@0xproject/react-shared'; import { ledgerEthereumBrowserClientFactoryAsync, LedgerSubprovider, + MetamaskSubprovider, RedundantSubprovider, RPCSubprovider, SignerSubprovider, @@ -27,8 +28,6 @@ import * as _ from 'lodash'; import * as moment from 'moment'; import * as React from 'react'; import contract = require('truffle-contract'); -import { tokenAddressOverrides } from 'ts/utils/token_address_overrides'; - import { BlockchainWatcher } from 'ts/blockchain_watcher'; import { AssetSendCompleted } from 'ts/components/flash_messages/asset_send_completed'; import { TransactionSubmitted } from 'ts/components/flash_messages/transaction_submitted'; @@ -54,6 +53,7 @@ import { backendClient } from 'ts/utils/backend_client'; import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; import { errorReporter } from 'ts/utils/error_reporter'; +import { tokenAddressOverrides } from 'ts/utils/token_address_overrides'; import { utils } from 'ts/utils/utils'; import FilterSubprovider = require('web3-provider-engine/subproviders/filters'); @@ -161,7 +161,13 @@ 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 Web3ProviderEngine(); - provider.addProvider(new SignerSubprovider(injectedWeb3.currentProvider)); + const providerName = this._getNameGivenProvider(injectedWeb3.currentProvider); + // Wrap Metamask in a compatability wrapper MetamaskSubprovider (to handle inconsistencies) + const signerSubprovider = + providerName === Providers.Metamask + ? new MetamaskSubprovider(injectedWeb3.currentProvider) + : new SignerSubprovider(injectedWeb3.currentProvider); + provider.addProvider(signerSubprovider); provider.addProvider(new FilterSubprovider()); const rpcSubproviders = _.map(publicNodeUrlsIfExistsForNetworkId, publicNodeUrl => { return new RPCSubprovider(publicNodeUrl); @@ -432,21 +438,7 @@ export class Blockchain { } this._showFlashMessageIfLedger(); const provider = this._contractWrappers.getProvider(); - const isLedgerSigner = !_.isUndefined(this._ledgerSubprovider); - const injectedProvider = Blockchain._getInjectedWeb3().currentProvider; - const isMetaMaskSigner = utils.getProviderType(injectedProvider) === Providers.Metamask; - let signerType = SignerType.Default; - if (isLedgerSigner) { - signerType = SignerType.Ledger; - } else if (isMetaMaskSigner) { - signerType = SignerType.Metamask; - } - const ecSignatureString = await signatureUtils.ecSignOrderHashAsync( - provider, - orderHash, - makerAddress, - signerType, - ); + const ecSignatureString = await signatureUtils.ecSignHashAsync(provider, orderHash, makerAddress); this._dispatcher.updateSignature(ecSignatureString); return ecSignatureString; } @@ -528,22 +528,6 @@ sinon "^4.0.0" websocket "^1.0.25" -"@0xproject/connect@^2.0.4": - version "2.0.4" - resolved "https://registry.npmjs.org/@0xproject/connect/-/connect-2.0.4.tgz#d61cd382edbb80120c8efce91dc85d2c668a5c5e" - dependencies: - "@0xproject/assert" "^1.0.11" - "@0xproject/json-schemas" "^1.0.4" - "@0xproject/order-utils" "^1.0.5" - "@0xproject/types" "^1.1.1" - "@0xproject/typescript-typings" "^2.0.2" - "@0xproject/utils" "^1.0.11" - lodash "^4.17.5" - query-string "^5.0.1" - sinon "^4.0.0" - uuid "^3.3.2" - websocket "^1.0.25" - "@0xproject/contract-wrappers@^0.1.1": version "0.1.1" resolved "https://registry.yarnpkg.com/@0xproject/contract-wrappers/-/contract-wrappers-0.1.1.tgz#8108d7ec051f202ef0cfa77c91c4ef994bf89881" @@ -716,7 +700,7 @@ lodash "4.17.10" web3 "0.20.6" -"@0xproject/utils@^1.0.11", "@0xproject/utils@^1.0.4": +"@0xproject/utils@^1.0.4": version "1.0.11" resolved "https://registry.yarnpkg.com/@0xproject/utils/-/utils-1.0.11.tgz#5b53e7d9d4dbe68e219049218c9db04e97c37429" dependencies: @@ -867,10 +851,6 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" -"@sindresorhus/is@^0.7.0": - version "0.7.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" - "@sinonjs/formatio@^2.0.0": version "2.0.0" resolved "http://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz#84db7e9eb5531df18a8c5e0bfb6e449e55e654b2" @@ -1223,6 +1203,13 @@ "@types/react" "*" redux "^3.6.0" +"@types/react-redux@^6.0.9": + version "6.0.9" + resolved "https://registry.npmjs.org/@types/react-redux/-/react-redux-6.0.9.tgz#96aa7f5b0716bcc3bfb36ceaa1223118d509f79a" + dependencies: + "@types/react" "*" + redux "^4.0.0" + "@types/react-router-dom@^4.0.4": version "4.2.6" resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-4.2.6.tgz#9f7eb3c0e6661a9607d878ff8675cc4ea95cd276" @@ -1254,16 +1241,11 @@ dependencies: "@types/react" "*" -"@types/react@*": - version "16.3.13" - resolved "https://registry.yarnpkg.com/@types/react/-/react-16.3.13.tgz#47d466462b774556c1174ea0eda22c0578643362" - dependencies: - csstype "^2.2.0" - -"@types/react@16.4.7": - version "16.4.7" - resolved "https://registry.npmjs.org/@types/react/-/react-16.4.7.tgz#f33f6d759a7e1833befa15224d68942d178a5a3f" +"@types/react@*", "@types/react@^16.4.16": + version "16.4.16" + resolved "https://registry.npmjs.org/@types/react/-/react-16.4.16.tgz#99f91b1200ae8c2062030402006d3b3c3a177043" dependencies: + "@types/prop-types" "*" csstype "^2.2.0" "@types/react@^16.4.2": @@ -1320,6 +1302,13 @@ version "0.2.1" resolved "https://registry.npmjs.org/@types/solidity-parser-antlr/-/solidity-parser-antlr-0.2.1.tgz#29ff386773a72a06c8a9c40c666d2a1cea53c2ed" +"@types/styled-components@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/styled-components/-/styled-components-4.0.0.tgz#33b47071885003878a62689e0883aa2fc59f970a" + dependencies: + "@types/node" "*" + "@types/react" "*" + "@types/tmp@^0.0.33": version "0.0.33" resolved "https://registry.yarnpkg.com/@types/tmp/-/tmp-0.0.33.tgz#1073c4bc824754ae3d10cfab88ab0237ba964e4d" @@ -1700,16 +1689,12 @@ ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" -ansi-styles@^3.1.0, ansi-styles@^3.2.0, ansi-styles@^3.2.1: +ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" dependencies: color-convert "^1.9.0" -ansi-styles@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.0.0.tgz#cb102df1c56f5123eab8b67cd7b98027a0279178" - ansi-wrap@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" @@ -1722,10 +1707,6 @@ antlr4@4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/antlr4/-/antlr4-4.7.0.tgz#297f956ddc06f83397fc0990ecf2e0cf20bfbbee" -any-observable@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.2.0.tgz#c67870058003579009083f54ac0abafb5c33d242" - any-promise@1.3.0, any-promise@^1.0.0, any-promise@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" @@ -1911,14 +1892,6 @@ assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" -ast-types@0.10.1: - version "0.10.1" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.10.1.tgz#f52fca9715579a14f841d67d7f8d25432ab6a3dd" - -ast-types@0.11.3: - version "0.11.3" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.11.3.tgz#c20757fe72ee71278ea0ff3d87e5c2ca30d9edf8" - astral-regex@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" @@ -1949,7 +1922,7 @@ async-limiter@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" -async@1.x, async@^1.4.0, async@^1.4.2, async@^1.5.0, async@^1.5.2: +async@1.x, async@^1.4.0, async@^1.4.2, async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" @@ -2043,17 +2016,11 @@ aws4@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" -axios@0.15.3: - version "0.15.3" - resolved "http://registry.npmjs.org/axios/-/axios-0.15.3.tgz#2c9d638b2e191a08ea1d6cc988eadd6ba5bdc053" - dependencies: - follow-redirects "1.0.0" - -axios@^0.17.0: - version "0.17.1" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.17.1.tgz#2d8e3e5d0bdbd7327f91bc814f5c57660f81824d" +axios@^0.18.0: + version "0.18.0" + resolved "http://registry.npmjs.org/axios/-/axios-0.18.0.tgz#32d53e4851efdc0a11993b6cd000789d70c05102" dependencies: - follow-redirects "^1.2.5" + follow-redirects "^1.3.0" is-buffer "^1.1.5" babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: @@ -2125,14 +2092,6 @@ babel-generator@^6.18.0, babel-generator@^6.26.0: source-map "^0.5.7" trim-right "^1.0.1" -babel-helper-bindify-decorators@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.24.1.tgz#14c19e5f142d7b47f19a52431e52b1ccbc40a330" - dependencies: - babel-runtime "^6.22.0" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - babel-helper-builder-binary-assignment-operator-visitor@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz#cce4517ada356f4220bcae8a02c2b346f9a56664" @@ -2167,15 +2126,6 @@ babel-helper-explode-assignable-expression@^6.24.1: babel-traverse "^6.24.1" babel-types "^6.24.1" -babel-helper-explode-class@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-explode-class/-/babel-helper-explode-class-6.24.1.tgz#7dc2a3910dee007056e1e31d640ced3d54eaa9eb" - dependencies: - babel-helper-bindify-decorators "^6.24.1" - babel-runtime "^6.22.0" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - babel-helper-function-name@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9" @@ -2279,39 +2229,11 @@ babel-plugin-syntax-async-functions@^6.8.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" -babel-plugin-syntax-async-generators@^6.5.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz#6bc963ebb16eccbae6b92b596eb7f35c342a8b9a" - -babel-plugin-syntax-class-constructor-call@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-constructor-call/-/babel-plugin-syntax-class-constructor-call-6.18.0.tgz#9cb9d39fe43c8600bec8146456ddcbd4e1a76416" - -babel-plugin-syntax-class-properties@^6.8.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz#d7eb23b79a317f8543962c505b827c7d6cac27de" - -babel-plugin-syntax-decorators@^6.13.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz#312563b4dbde3cc806cee3e416cceeaddd11ac0b" - -babel-plugin-syntax-dynamic-import@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz#8d6a26229c83745a9982a441051572caa179b1da" - babel-plugin-syntax-exponentiation-operator@^6.8.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de" -babel-plugin-syntax-export-extensions@^6.8.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-export-extensions/-/babel-plugin-syntax-export-extensions-6.13.0.tgz#70a1484f0f9089a4e84ad44bac353c95b9b12721" - -babel-plugin-syntax-flow@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz#4c3ab20a2af26aa20cd25995c398c4eb70310c8d" - -babel-plugin-syntax-object-rest-spread@^6.13.0, babel-plugin-syntax-object-rest-spread@^6.8.0: +babel-plugin-syntax-object-rest-spread@^6.13.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5" @@ -2319,15 +2241,7 @@ babel-plugin-syntax-trailing-function-commas@^6.22.0: version "6.22.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3" -babel-plugin-transform-async-generator-functions@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz#f058900145fd3e9907a6ddf28da59f215258a5db" - dependencies: - babel-helper-remap-async-to-generator "^6.24.1" - babel-plugin-syntax-async-generators "^6.5.0" - babel-runtime "^6.22.0" - -babel-plugin-transform-async-to-generator@^6.22.0, babel-plugin-transform-async-to-generator@^6.24.1: +babel-plugin-transform-async-to-generator@^6.22.0: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz#6536e378aff6cb1d5517ac0e40eb3e9fc8d08761" dependencies: @@ -2335,33 +2249,6 @@ babel-plugin-transform-async-to-generator@^6.22.0, babel-plugin-transform-async- babel-plugin-syntax-async-functions "^6.8.0" babel-runtime "^6.22.0" -babel-plugin-transform-class-constructor-call@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-constructor-call/-/babel-plugin-transform-class-constructor-call-6.24.1.tgz#80dc285505ac067dcb8d6c65e2f6f11ab7765ef9" - dependencies: - babel-plugin-syntax-class-constructor-call "^6.18.0" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-plugin-transform-class-properties@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz#6a79763ea61d33d36f37b611aa9def81a81b46ac" - dependencies: - babel-helper-function-name "^6.24.1" - babel-plugin-syntax-class-properties "^6.8.0" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-plugin-transform-decorators@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.24.1.tgz#788013d8f8c6b5222bdf7b344390dfd77569e24d" - dependencies: - babel-helper-explode-class "^6.24.1" - babel-plugin-syntax-decorators "^6.13.0" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-types "^6.24.1" - babel-plugin-transform-es2015-arrow-functions@^6.22.0: version "6.22.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221" @@ -2374,7 +2261,7 @@ babel-plugin-transform-es2015-block-scoped-functions@^6.22.0: dependencies: babel-runtime "^6.22.0" -babel-plugin-transform-es2015-block-scoping@^6.23.0, babel-plugin-transform-es2015-block-scoping@^6.24.1: +babel-plugin-transform-es2015-block-scoping@^6.23.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz#d70f5299c1308d05c12f463813b0a09e73b1895f" dependencies: @@ -2384,7 +2271,7 @@ babel-plugin-transform-es2015-block-scoping@^6.23.0, babel-plugin-transform-es20 babel-types "^6.26.0" lodash "^4.17.4" -babel-plugin-transform-es2015-classes@^6.23.0, babel-plugin-transform-es2015-classes@^6.24.1: +babel-plugin-transform-es2015-classes@^6.23.0: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db" dependencies: @@ -2398,33 +2285,33 @@ babel-plugin-transform-es2015-classes@^6.23.0, babel-plugin-transform-es2015-cla babel-traverse "^6.24.1" babel-types "^6.24.1" -babel-plugin-transform-es2015-computed-properties@^6.22.0, babel-plugin-transform-es2015-computed-properties@^6.24.1: +babel-plugin-transform-es2015-computed-properties@^6.22.0: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3" dependencies: babel-runtime "^6.22.0" babel-template "^6.24.1" -babel-plugin-transform-es2015-destructuring@^6.22.0, babel-plugin-transform-es2015-destructuring@^6.23.0: +babel-plugin-transform-es2015-destructuring@^6.23.0: version "6.23.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d" dependencies: babel-runtime "^6.22.0" -babel-plugin-transform-es2015-duplicate-keys@^6.22.0, babel-plugin-transform-es2015-duplicate-keys@^6.24.1: +babel-plugin-transform-es2015-duplicate-keys@^6.22.0: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz#73eb3d310ca969e3ef9ec91c53741a6f1576423e" dependencies: babel-runtime "^6.22.0" babel-types "^6.24.1" -babel-plugin-transform-es2015-for-of@^6.22.0, babel-plugin-transform-es2015-for-of@^6.23.0: +babel-plugin-transform-es2015-for-of@^6.23.0: version "6.23.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691" dependencies: babel-runtime "^6.22.0" -babel-plugin-transform-es2015-function-name@^6.22.0, babel-plugin-transform-es2015-function-name@^6.24.1: +babel-plugin-transform-es2015-function-name@^6.22.0: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b" dependencies: @@ -2455,7 +2342,7 @@ babel-plugin-transform-es2015-modules-commonjs@^6.23.0, babel-plugin-transform-e babel-template "^6.26.0" babel-types "^6.26.0" -babel-plugin-transform-es2015-modules-systemjs@^6.23.0, babel-plugin-transform-es2015-modules-systemjs@^6.24.1: +babel-plugin-transform-es2015-modules-systemjs@^6.23.0: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz#ff89a142b9119a906195f5f106ecf305d9407d23" dependencies: @@ -2463,7 +2350,7 @@ babel-plugin-transform-es2015-modules-systemjs@^6.23.0, babel-plugin-transform-e babel-runtime "^6.22.0" babel-template "^6.24.1" -babel-plugin-transform-es2015-modules-umd@^6.23.0, babel-plugin-transform-es2015-modules-umd@^6.24.1: +babel-plugin-transform-es2015-modules-umd@^6.23.0: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz#ac997e6285cd18ed6176adb607d602344ad38468" dependencies: @@ -2471,14 +2358,14 @@ babel-plugin-transform-es2015-modules-umd@^6.23.0, babel-plugin-transform-es2015 babel-runtime "^6.22.0" babel-template "^6.24.1" -babel-plugin-transform-es2015-object-super@^6.22.0, babel-plugin-transform-es2015-object-super@^6.24.1: +babel-plugin-transform-es2015-object-super@^6.22.0: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz#24cef69ae21cb83a7f8603dad021f572eb278f8d" dependencies: babel-helper-replace-supers "^6.24.1" babel-runtime "^6.22.0" -babel-plugin-transform-es2015-parameters@^6.23.0, babel-plugin-transform-es2015-parameters@^6.24.1: +babel-plugin-transform-es2015-parameters@^6.23.0: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b" dependencies: @@ -2489,7 +2376,7 @@ babel-plugin-transform-es2015-parameters@^6.23.0, babel-plugin-transform-es2015- babel-traverse "^6.24.1" babel-types "^6.24.1" -babel-plugin-transform-es2015-shorthand-properties@^6.22.0, babel-plugin-transform-es2015-shorthand-properties@^6.24.1: +babel-plugin-transform-es2015-shorthand-properties@^6.22.0: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0" dependencies: @@ -2502,7 +2389,7 @@ babel-plugin-transform-es2015-spread@^6.22.0: dependencies: babel-runtime "^6.22.0" -babel-plugin-transform-es2015-sticky-regex@^6.22.0, babel-plugin-transform-es2015-sticky-regex@^6.24.1: +babel-plugin-transform-es2015-sticky-regex@^6.22.0: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc" dependencies: @@ -2516,13 +2403,13 @@ babel-plugin-transform-es2015-template-literals@^6.22.0: dependencies: babel-runtime "^6.22.0" -babel-plugin-transform-es2015-typeof-symbol@^6.22.0, babel-plugin-transform-es2015-typeof-symbol@^6.23.0: +babel-plugin-transform-es2015-typeof-symbol@^6.23.0: version "6.23.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz#dec09f1cddff94b52ac73d505c84df59dcceb372" dependencies: babel-runtime "^6.22.0" -babel-plugin-transform-es2015-unicode-regex@^6.22.0, babel-plugin-transform-es2015-unicode-regex@^6.24.1: +babel-plugin-transform-es2015-unicode-regex@^6.22.0: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz#d38b12f42ea7323f729387f18a7c5ae1faeb35e9" dependencies: @@ -2530,7 +2417,7 @@ babel-plugin-transform-es2015-unicode-regex@^6.22.0, babel-plugin-transform-es20 babel-runtime "^6.22.0" regexpu-core "^2.0.0" -babel-plugin-transform-exponentiation-operator@^6.22.0, babel-plugin-transform-exponentiation-operator@^6.24.1: +babel-plugin-transform-exponentiation-operator@^6.22.0: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz#2ab0c9c7f3098fa48907772bb813fe41e8de3a0e" dependencies: @@ -2538,28 +2425,7 @@ babel-plugin-transform-exponentiation-operator@^6.22.0, babel-plugin-transform-e babel-plugin-syntax-exponentiation-operator "^6.8.0" babel-runtime "^6.22.0" -babel-plugin-transform-export-extensions@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-export-extensions/-/babel-plugin-transform-export-extensions-6.22.0.tgz#53738b47e75e8218589eea946cbbd39109bbe653" - dependencies: - babel-plugin-syntax-export-extensions "^6.8.0" - babel-runtime "^6.22.0" - -babel-plugin-transform-flow-strip-types@^6.8.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz#84cb672935d43714fdc32bce84568d87441cf7cf" - dependencies: - babel-plugin-syntax-flow "^6.18.0" - babel-runtime "^6.22.0" - -babel-plugin-transform-object-rest-spread@^6.22.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz#0f36692d50fef6b7e2d4b3ac1478137a963b7b06" - dependencies: - babel-plugin-syntax-object-rest-spread "^6.8.0" - babel-runtime "^6.26.0" - -babel-plugin-transform-regenerator@^6.22.0, babel-plugin-transform-regenerator@^6.24.1: +babel-plugin-transform-regenerator@^6.22.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz#e0703696fbde27f0a3efcacf8b4dca2f7b3a8f2f" dependencies: @@ -2607,35 +2473,6 @@ babel-preset-env@^1.3.2: invariant "^2.2.2" semver "^5.3.0" -babel-preset-es2015@^6.9.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz#d44050d6bc2c9feea702aaf38d727a0210538939" - dependencies: - babel-plugin-check-es2015-constants "^6.22.0" - babel-plugin-transform-es2015-arrow-functions "^6.22.0" - babel-plugin-transform-es2015-block-scoped-functions "^6.22.0" - babel-plugin-transform-es2015-block-scoping "^6.24.1" - babel-plugin-transform-es2015-classes "^6.24.1" - babel-plugin-transform-es2015-computed-properties "^6.24.1" - babel-plugin-transform-es2015-destructuring "^6.22.0" - babel-plugin-transform-es2015-duplicate-keys "^6.24.1" - babel-plugin-transform-es2015-for-of "^6.22.0" - babel-plugin-transform-es2015-function-name "^6.24.1" - babel-plugin-transform-es2015-literals "^6.22.0" - babel-plugin-transform-es2015-modules-amd "^6.24.1" - babel-plugin-transform-es2015-modules-commonjs "^6.24.1" - babel-plugin-transform-es2015-modules-systemjs "^6.24.1" - babel-plugin-transform-es2015-modules-umd "^6.24.1" - babel-plugin-transform-es2015-object-super "^6.24.1" - babel-plugin-transform-es2015-parameters "^6.24.1" - babel-plugin-transform-es2015-shorthand-properties "^6.24.1" - babel-plugin-transform-es2015-spread "^6.22.0" - babel-plugin-transform-es2015-sticky-regex "^6.24.1" - babel-plugin-transform-es2015-template-literals "^6.22.0" - babel-plugin-transform-es2015-typeof-symbol "^6.22.0" - babel-plugin-transform-es2015-unicode-regex "^6.24.1" - babel-plugin-transform-regenerator "^6.24.1" - babel-preset-jest@^23.2.0: version "23.2.0" resolved "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-23.2.0.tgz#8ec7a03a138f001a1a8fb1e8113652bf1a55da46" @@ -2643,34 +2480,7 @@ babel-preset-jest@^23.2.0: babel-plugin-jest-hoist "^23.2.0" babel-plugin-syntax-object-rest-spread "^6.13.0" -babel-preset-stage-1@^6.5.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-preset-stage-1/-/babel-preset-stage-1-6.24.1.tgz#7692cd7dcd6849907e6ae4a0a85589cfb9e2bfb0" - dependencies: - babel-plugin-transform-class-constructor-call "^6.24.1" - babel-plugin-transform-export-extensions "^6.22.0" - babel-preset-stage-2 "^6.24.1" - -babel-preset-stage-2@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-preset-stage-2/-/babel-preset-stage-2-6.24.1.tgz#d9e2960fb3d71187f0e64eec62bc07767219bdc1" - dependencies: - babel-plugin-syntax-dynamic-import "^6.18.0" - babel-plugin-transform-class-properties "^6.24.1" - babel-plugin-transform-decorators "^6.24.1" - babel-preset-stage-3 "^6.24.1" - -babel-preset-stage-3@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-preset-stage-3/-/babel-preset-stage-3-6.24.1.tgz#836ada0a9e7a7fa37cb138fb9326f87934a48395" - dependencies: - babel-plugin-syntax-trailing-function-commas "^6.22.0" - babel-plugin-transform-async-generator-functions "^6.24.1" - babel-plugin-transform-async-to-generator "^6.24.1" - babel-plugin-transform-exponentiation-operator "^6.24.1" - babel-plugin-transform-object-rest-spread "^6.22.0" - -babel-register@^6.26.0, babel-register@^6.9.0: +babel-register@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071" dependencies: @@ -2729,14 +2539,10 @@ babelify@^7.3.0: babel-core "^6.0.14" object-assign "^4.0.0" -babylon@^6.1.21, babylon@^6.17.3, babylon@^6.18.0: +babylon@^6.1.21, babylon@^6.18.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" -babylon@^7.0.0-beta.30: - version "7.0.0-beta.46" - resolved "https://registry.yarnpkg.com/babylon/-/babylon-7.0.0-beta.46.tgz#b6ddaba81bbb130313932757ff9c195d527088b6" - backoff@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/backoff/-/backoff-2.5.0.tgz#f616eda9d3e4b66b8ca7fca79f695722c5f8e26f" @@ -2883,10 +2689,6 @@ binary-extensions@^1.0.0: version "1.11.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205" -binaryextensions@2: - version "2.1.1" - resolved "https://registry.yarnpkg.com/binaryextensions/-/binaryextensions-2.1.1.tgz#3209a51ca4a4ad541a3b8d3d6a6d5b83a2485935" - bindings@^1.2.1, bindings@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.3.0.tgz#b346f6ecf6a95f5a815c5839fc7cdb22502f1ed7" @@ -3094,13 +2896,6 @@ brorand@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" -brotli-size@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/brotli-size/-/brotli-size-0.0.1.tgz#8c1aeea01cd22f359b048951185bd539ff0c829f" - dependencies: - duplexer "^0.1.1" - iltorb "^1.0.9" - browser-process-hrtime@^0.1.2: version "0.1.3" resolved "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz#616f00faef1df7ec1b5bf9cfe2bdc3170f26c7b4" @@ -3289,6 +3084,13 @@ buffer@^5.0.3, buffer@^5.0.5: base64-js "^1.0.2" ieee754 "^1.1.4" +buffer@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.2.1.tgz#dd57fa0f109ac59c602479044dca7b8b3d0b71d6" + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + builtin-modules@^1.0.0, builtin-modules@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" @@ -3301,19 +3103,18 @@ builtins@^1.0.3: version "1.0.3" resolved "http://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" -bundlesize@^0.17.0: - version "0.17.0" - resolved "https://registry.yarnpkg.com/bundlesize/-/bundlesize-0.17.0.tgz#212ae5731ab0554d2acd509d23e1de18640b2008" +bundlewatch@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/bundlewatch/-/bundlewatch-0.2.1.tgz#3bf5279f49c098c7b27c31f7c172397154eb2e65" dependencies: - axios "^0.17.0" - brotli-size "0.0.1" + axios "^0.18.0" bytes "^3.0.0" - ci-env "^1.4.0" - commander "^2.11.0" - github-build "^1.2.0" + chalk "^2.4.0" + commander "^2.15.1" glob "^7.1.2" gzip-size "^4.0.0" - prettycli "^1.4.3" + jsonpack "^1.1.5" + lodash.merge "^4.6.1" read-pkg-up "^3.0.0" byline@^5.0.0: @@ -3388,18 +3189,6 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" -cacheable-request@^2.1.1: - version "2.1.4" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-2.1.4.tgz#0d808801b6342ad33c91df9d0b44dc09b91e5c3d" - dependencies: - clone-response "1.0.2" - get-stream "3.0.0" - http-cache-semantics "3.8.1" - keyv "3.0.0" - lowercase-keys "1.0.0" - normalize-url "2.0.1" - responselike "1.0.2" - cachedown@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/cachedown/-/cachedown-1.0.0.tgz#d43f036e4510696b31246d7db31ebf0f7ac32d15" @@ -3542,14 +3331,6 @@ chain-function@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/chain-function/-/chain-function-1.0.0.tgz#0d4ab37e7e18ead0bdc47b920764118ce58733dc" -chalk@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.1.0.tgz#ac5becf14fa21b99c6c92ca7a7d7cfd5b17e743e" - dependencies: - ansi-styles "^3.1.0" - escape-string-regexp "^1.0.5" - supports-color "^4.0.0" - chalk@2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65" @@ -3576,7 +3357,7 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.3 escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^2.4.1: +chalk@^2.4.0, chalk@^2.4.1: version "2.4.1" resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" dependencies: @@ -3584,14 +3365,6 @@ chalk@^2.4.1: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@~0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.4.0.tgz#5199a3ddcd0c1efe23bc08c1b027b06176e0c64f" - dependencies: - ansi-styles "~1.0.0" - has-color "~0.1.0" - strip-ansi "~0.1.0" - change-case@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/change-case/-/change-case-3.0.2.tgz#fd48746cce02f03f0a672577d1d3a8dc2eceb037" @@ -3714,10 +3487,6 @@ chrome-trace-event@^1.0.0: dependencies: tslib "^1.9.0" -ci-env@^1.4.0: - version "1.6.1" - resolved "https://registry.yarnpkg.com/ci-env/-/ci-env-1.6.1.tgz#3e3ef4fc528a2825397f912cfa30cde17ec364cc" - ci-info@^1.0.0: version "1.1.3" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.1.3.tgz#710193264bb05c77b8c90d02f5aaf22216a667b2" @@ -3795,12 +3564,6 @@ cli-table2@0.2.0: optionalDependencies: colors "^1.1.2" -cli-table@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.1.tgz#f53b05266a8b1a0b934b3d0821e6e2dc5914ae23" - dependencies: - colors "1.0.3" - cli-truncate@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" @@ -3852,12 +3615,6 @@ clone-buffer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" -clone-response@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" - dependencies: - mimic-response "^1.0.0" - clone-stats@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1" @@ -3965,7 +3722,7 @@ colors@0.5.x: version "0.5.1" resolved "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz#7d0023eaeb154e8ee9fce75dcb923d0ed1667774" -colors@1.0.3, colors@1.0.x: +colors@1.0.x: version "1.0.3" resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" @@ -4006,9 +3763,9 @@ commander@2.15.1, commander@^2.12.1, commander@^2.8.1: version "2.15.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" -commander@^2.11.0: - version "2.18.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970" +commander@^2.15.1: + version "2.19.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" commander@^2.7.1, commander@~2.17.1: version "2.17.1" @@ -4274,10 +4031,6 @@ core-js@^2.4.0, core-js@^2.5.0: version "2.5.5" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.5.tgz#b14dde936c640c0579a6b50cabcc132dd6127e3b" -core-js@^2.4.1: - version "2.5.6" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.6.tgz#0fe6d45bf3cac3ac364a9d72de7576f4eb221b9d" - core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -4593,10 +4346,6 @@ dargs@^4.0.1: dependencies: number-is-nan "^1.0.0" -dargs@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/dargs/-/dargs-5.1.0.tgz#ec7ea50c78564cd36c9d5ec18f66329fade27829" - dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -4623,7 +4372,7 @@ dateformat@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-2.2.0.tgz#4065e2013cf9fb916ddfd82efb506ad4c6769062" -dateformat@^3.0.0, dateformat@^3.0.3: +dateformat@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" @@ -4770,10 +4519,6 @@ deep-equal@~0.2.1: version "0.2.2" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-0.2.2.tgz#84b745896f34c684e98f2ce0e42abaf43bba017d" -deep-extend@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.5.1.tgz#b894a9dd90d3023fbf1c55a394fb858eb2066f1f" - deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" @@ -4933,10 +4678,6 @@ destroy@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" -detect-conflict@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/detect-conflict/-/detect-conflict-1.0.1.tgz#088657a66a961c05019db7c4230883b1c6b4176e" - detect-file@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" @@ -4951,10 +4692,6 @@ detect-indent@^5.0.0: version "5.0.0" resolved "http://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" -detect-libc@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-0.2.0.tgz#47fdf567348a17ec25fcbf0b9e446348a76f9fb5" - detect-libc@^1.0.2, detect-libc@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" @@ -4978,7 +4715,7 @@ diff@3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/diff/-/diff-3.3.1.tgz#aa8567a6eed03c531fc89d3f711cd0e5259dec75" -diff@3.5.0, diff@^3.1.0, diff@^3.2.0, diff@^3.3.1, diff@^3.5.0: +diff@3.5.0, diff@^3.1.0, diff@^3.2.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" @@ -5177,10 +4914,6 @@ ecc-jsbn@~0.1.1: dependencies: jsbn "~0.1.0" -editions@^1.3.3: - version "1.3.4" - resolved "https://registry.yarnpkg.com/editions/-/editions-1.3.4.tgz#3662cb592347c3168eb8e498a0ff73271d67f50b" - editor@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/editor/-/editor-1.0.0.tgz#60c7f87bd62bcc6a894fa8ccd6afb7823a24f742" @@ -5189,10 +4922,6 @@ ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" -ejs@^2.5.9: - version "2.6.1" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.6.1.tgz#498ec0d495655abc6f23cd61868d926464071aa0" - electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.30: version "1.3.42" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.42.tgz#95c33bf01d0cc405556aec899fe61fd4d76ea0f9" @@ -5277,10 +5006,6 @@ entities@^1.1.1, entities@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" -envinfo@^4.4.2: - version "4.4.2" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-4.4.2.tgz#472c49f3a8b9bca73962641ce7cb692bf623cd1c" - enzyme-adapter-react-16@^1.5.0: version "1.5.0" resolved "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.5.0.tgz#50af8d76a45fe0915de932bd95d34cdca75c0be3" @@ -5337,13 +5062,6 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -error@^7.0.2: - version "7.0.2" - resolved "https://registry.yarnpkg.com/error/-/error-7.0.2.tgz#a5f75fff4d9926126ddac0ea5dc38e689153cb02" - dependencies: - string-template "~0.2.1" - xtend "~4.0.0" - es-abstract@^1.10.0, es-abstract@^1.5.1, es-abstract@^1.6.1: version "1.12.0" resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz#9dbbdd27c6856f0001421ca18782d786bf8a6165" @@ -5515,7 +5233,7 @@ esprima@^3.1.3: version "3.1.3" resolved "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" -esprima@^4.0.0, esprima@~4.0.0: +esprima@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804" @@ -5654,6 +5372,17 @@ eth-sig-util@^1.4.2: ethereumjs-abi "git+https://github.com/ethereumjs/ethereumjs-abi.git" ethereumjs-util "^5.1.1" +eth-sig-util@^2.0.2: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eth-sig-util/-/eth-sig-util-2.1.0.tgz#33e60e5486897a2ddeb4bf5a0993b2c6d5cc9e19" + dependencies: + buffer "^5.2.1" + elliptic "^6.4.0" + ethereumjs-abi "0.6.5" + ethereumjs-util "^5.1.1" + tweetnacl "^1.0.0" + tweetnacl-util "^0.15.0" + eth-tx-summary@^3.1.2: version "3.2.1" resolved "https://registry.yarnpkg.com/eth-tx-summary/-/eth-tx-summary-3.2.1.tgz#0c2d5e4c57d2511614f85b9b583c32fa2924166c" @@ -5691,7 +5420,7 @@ ethereum-types@^0.0.2: "@types/node" "^8.0.53" bignumber.js "~4.1.0" -ethereumjs-abi@0.6.5: +ethereumjs-abi@0.6.5, ethereumjs-abi@^0.6.5: version "0.6.5" resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.5.tgz#5a637ef16ab43473fa72a29ad90871405b3f5241" dependencies: @@ -5748,7 +5477,7 @@ ethereumjs-blockstream@6.0.0: source-map-support "0.5.6" uuid "3.2.1" -ethereumjs-tx@0xProject/ethereumjs-tx#fake-tx-include-signature-by-default: +ethereumjs-tx@0xProject/ethereumjs-tx#fake-tx-include-signature-by-default, ethereumjs-tx@1.3.4: version "1.3.4" resolved "https://codeload.github.com/0xProject/ethereumjs-tx/tar.gz/29d1153889c389591f74b2401da8a0c6ad40f9a7" dependencies: @@ -5855,9 +5584,9 @@ ethers@3.0.22: uuid "2.0.1" xmlhttprequest "1.8.0" -ethers@4.0.0-beta.14: - version "4.0.0-beta.14" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.0-beta.14.tgz#76aa9257b9c93a7604ff4dc11f2a445d07f6459d" +ethers@~4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.4.tgz#d3f85e8b27f4b59537e06526439b0fb15b44dc65" dependencies: "@types/node" "^10.3.2" aes-js "3.0.0" @@ -5865,7 +5594,7 @@ ethers@4.0.0-beta.14: elliptic "6.3.3" hash.js "1.1.3" js-sha3 "0.5.7" - scrypt-js "2.0.3" + scrypt-js "2.0.4" setimmediate "1.0.4" uuid "2.0.1" xmlhttprequest "1.8.0" @@ -6398,12 +6127,6 @@ first-chunk-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz#59bfb50cd905f60d7c394cd3d9acaab4e6ad934e" -first-chunk-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz#1bdecdb8e083c0664b91945581577a43a9f31d70" - dependencies: - readable-stream "^2.0.2" - flagged-respawn@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-1.0.0.tgz#4e79ae9b2eb38bf86b3bb56bf3e0a56aa5fcabd7" @@ -6421,10 +6144,6 @@ flatten@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" -flow-parser@^0.*: - version "0.72.0" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.72.0.tgz#6c8041e76ac7d0be1a71ce29c00cd1435fb6013c" - flush-write-stream@^1.0.0, flush-write-stream@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.3.tgz#c5d586ef38af6097650b49bc41b55fabb19f35bd" @@ -6432,13 +6151,7 @@ flush-write-stream@^1.0.0, flush-write-stream@^1.0.2: inherits "^2.0.1" readable-stream "^2.0.4" -follow-redirects@1.0.0: - version "1.0.0" - resolved "http://registry.npmjs.org/follow-redirects/-/follow-redirects-1.0.0.tgz#8e34298cbd2e176f254effec75a1c78cc849fd37" - dependencies: - debug "^2.2.0" - -follow-redirects@^1.2.5: +follow-redirects@^1.3.0: version "1.5.8" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.8.tgz#1dbfe13e45ad969f813e86c00e5296f525c885a1" dependencies: @@ -6515,7 +6228,7 @@ fresh@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" -from2@^2.1.0, from2@^2.1.1: +from2@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" dependencies: @@ -6641,12 +6354,11 @@ functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" -ganache-cli@6.1.3: - version "6.1.3" - resolved "https://registry.yarnpkg.com/ganache-cli/-/ganache-cli-6.1.3.tgz#40323e56c878b5c7275830cb8a4fa75a32f6d468" +ganache-cli@6.1.8: + version "6.1.8" + resolved "https://registry.yarnpkg.com/ganache-cli/-/ganache-cli-6.1.8.tgz#49a8a331683a9652183f82ef1378d17e1814fcd3" dependencies: source-map-support "^0.5.3" - webpack-cli "^2.0.9" ganache-core@0xProject/ganache-core#monorepo-dep: version "2.1.2" @@ -6685,6 +6397,46 @@ ganache-core@0xProject/ganache-core#monorepo-dep: websocket "^1.0.24" yargs "^7.0.2" +ganache-core@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ganache-core/-/ganache-core-2.2.1.tgz#c0125d77d3e28d627a812dd002dac21e1d2cc8b7" + dependencies: + abstract-leveldown "^3.0.0" + async "^2.5.0" + bip39 "~2.4.0" + bn.js "4.11.6" + cachedown "^1.0.0" + chai "^3.5.0" + clone "^2.1.1" + eth-sig-util "^2.0.2" + ethereumjs-abi "^0.6.5" + ethereumjs-account "~2.0.4" + ethereumjs-block "~1.2.2" + ethereumjs-tx "1.3.4" + ethereumjs-util "^5.2.0" + ethereumjs-vm "2.3.5" + ethereumjs-wallet "0.6.0" + fake-merkle-patricia-tree "~1.0.1" + heap "~0.2.6" + js-scrypt "^0.2.0" + level-sublevel "^6.6.1" + levelup "^1.1.0" + localstorage-down "^0.6.7" + lodash "^4.17.5" + merkle-patricia-tree "^2.2.0" + pify "^3.0.0" + prepend-file "^1.3.1" + request "^2.87.0" + seedrandom "~2.4.2" + shebang-loader "0.0.1" + solc "0.4.24" + temp "^0.8.3" + tmp "0.0.31" + web3 "^1.0.0-beta.34" + web3-provider-engine "^14.0.6" + websocket "^1.0.24" + yargs "^7.0.2" + gauge@~1.2.5: version "1.2.7" resolved "https://registry.yarnpkg.com/gauge/-/gauge-1.2.7.tgz#e9cec5483d3d4ee0ef44b60a7d99e4935e136d93" @@ -6748,10 +6500,6 @@ get-stdin@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398" -get-stream@3.0.0, get-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" - get-stream@^2.2.0: version "2.3.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de" @@ -6759,6 +6507,10 @@ get-stream@^2.2.0: object-assign "^4.0.1" pinkie-promise "^2.0.0" +get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -6769,13 +6521,6 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" -gh-got@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/gh-got/-/gh-got-6.0.0.tgz#d74353004c6ec466647520a10bd46f7299d268d0" - dependencies: - got "^7.0.0" - is-plain-obj "^1.1.0" - ghauth@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/ghauth/-/ghauth-2.0.1.tgz#79b7d68b0bcf8e7d0852a23b147539dfd314acf6" @@ -6816,12 +6561,6 @@ gitconfiglocal@^1.0.0: dependencies: ini "^1.3.2" -github-build@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/github-build/-/github-build-1.2.0.tgz#b0bdb705ae4088218577e863c1a301030211051f" - dependencies: - axios "0.15.3" - github-from-package@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" @@ -6832,19 +6571,6 @@ github-url-to-object@^1.4.2: dependencies: is-url "^1.1.0" -github-username@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/github-username/-/github-username-4.1.0.tgz#cbe280041883206da4212ae9e4b5f169c30bf417" - dependencies: - gh-got "^6.0.0" - -glob-all@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/glob-all/-/glob-all-3.1.0.tgz#8913ddfb5ee1ac7812656241b03d5217c64b02ab" - dependencies: - glob "^7.0.5" - yargs "~1.2.6" - glob-base@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" @@ -7009,7 +6735,7 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" -globby@^8.0.0, globby@^8.0.1: +globby@^8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/globby/-/globby-8.0.1.tgz#b5ad48b8aa80b35b814fc1281ecc851f1d2b5b50" dependencies: @@ -7041,7 +6767,7 @@ good-listener@^1.2.2: dependencies: delegate "^3.1.2" -got@7.1.0, got@^7.0.0, got@^7.1.0: +got@7.1.0, got@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" dependencies: @@ -7076,28 +6802,6 @@ got@^6.7.1: unzip-response "^2.0.1" url-parse-lax "^1.0.0" -got@^8.2.0: - version "8.3.1" - resolved "https://registry.yarnpkg.com/got/-/got-8.3.1.tgz#093324403d4d955f5a16a7a8d39955d055ae10ed" - dependencies: - "@sindresorhus/is" "^0.7.0" - cacheable-request "^2.1.1" - decompress-response "^3.3.0" - duplexer3 "^0.1.4" - get-stream "^3.0.0" - into-stream "^3.1.0" - is-retry-allowed "^1.1.0" - isurl "^1.0.0-alpha5" - lowercase-keys "^1.0.0" - mimic-response "^1.0.0" - p-cancelable "^0.4.0" - p-timeout "^2.0.1" - pify "^3.0.0" - safe-buffer "^5.1.1" - timed-out "^4.0.1" - url-parse-lax "^3.0.0" - url-to-options "^1.0.1" - graceful-fs@^3.0.0: version "3.0.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-3.0.11.tgz#7613c778a1afea62f25c630a086d7f3acbbdd818" @@ -7116,12 +6820,6 @@ graceful-fs@~1.2.0: version "1.0.1" resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" -grouped-queue@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/grouped-queue/-/grouped-queue-0.3.3.tgz#c167d2a5319c5a0e0964ef6a25b7c2df8996c85c" - dependencies: - lodash "^4.17.2" - growl@1.10.3: version "1.10.3" resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.3.tgz#1926ba90cf3edfe2adb4927f5880bc22c66c790f" @@ -7241,10 +6939,6 @@ has-ansi@^2.0.0: dependencies: ansi-regex "^2.0.0" -has-color@~0.1.0: - version "0.1.7" - resolved "https://registry.yarnpkg.com/has-color/-/has-color-0.1.7.tgz#67144a5260c34fc3cca677d041daf52fe7b78b2f" - has-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" @@ -7481,10 +7175,6 @@ htmlparser2@^3.9.0, htmlparser2@^3.9.1: inherits "^2.0.1" readable-stream "^2.0.2" -http-cache-semantics@3.8.1: - version "3.8.1" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" - http-deceiver@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" @@ -7644,15 +7334,6 @@ ignore@^3.3.5: version "3.3.7" resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.7.tgz#612289bfb3c220e186a58118618d5be8c1bab021" -iltorb@^1.0.9: - version "1.3.10" - resolved "https://registry.yarnpkg.com/iltorb/-/iltorb-1.3.10.tgz#a0d9e4e7d52bf510741442236cbe0cc4230fc9f8" - dependencies: - detect-libc "^0.2.0" - nan "^2.6.2" - node-gyp "^3.6.2" - prebuild-install "^2.3.0" - image-size@~0.5.0: version "0.5.5" resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c" @@ -7786,7 +7467,7 @@ inquirer@^3.0.6, inquirer@^3.3.0: strip-ansi "^4.0.0" through "^2.3.6" -inquirer@^5.1.0, inquirer@^5.2.0: +inquirer@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-5.2.0.tgz#db350c2b73daca77ff1243962e9f22f099685726" dependencies: @@ -7822,17 +7503,10 @@ internal-ip@^3.0.1: default-gateway "^2.6.0" ipaddr.js "^1.5.2" -interpret@^1.0.0, interpret@^1.0.4, interpret@^1.1.0: +interpret@^1.0.0, interpret@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" -into-stream@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/into-stream/-/into-stream-3.1.0.tgz#96fb0a936c12babd6ff1752a17d05616abd094c6" - dependencies: - from2 "^2.1.1" - p-is-promise "^1.1.0" - invariant@^2.0.0, invariant@^2.2.1, invariant@^2.2.2, invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" @@ -8122,12 +7796,6 @@ is-object@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470" -is-observable@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/is-observable/-/is-observable-0.2.0.tgz#b361311d83c6e5d726cabf5e250b0237106f5ae2" - dependencies: - symbol-observable "^0.2.2" - is-odd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-odd/-/is-odd-2.0.0.tgz#7646624671fd7ea558ccd9a2795182f2958f1b24" @@ -8192,16 +7860,10 @@ is-resolvable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" -is-retry-allowed@^1.0.0, is-retry-allowed@^1.1.0: +is-retry-allowed@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" -is-scoped@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-scoped/-/is-scoped-1.0.0.tgz#449ca98299e713038256289ecb2b540dc437cb30" - dependencies: - scoped-regex "^1.0.0" - is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -8286,10 +7948,6 @@ isarray@^2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/isarray/-/isarray-2.0.4.tgz#38e7bcbb0f3ba1b7933c86ba1894ddfc3781bbb7" -isbinaryfile@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.2.tgz#4a3e974ec0cba9004d3fc6cde7209ea69368a621" - isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -8444,14 +8102,6 @@ istanbul@^0.4.5: which "^1.1.1" wordwrap "^1.0.0" -istextorbinary@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/istextorbinary/-/istextorbinary-2.2.1.tgz#a5231a08ef6dd22b268d0895084cf8d58b5bec53" - dependencies: - binaryextensions "2" - editions "^1.3.3" - textextensions "2" - isurl@^1.0.0-alpha5: version "1.0.0" resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" @@ -8816,46 +8466,6 @@ jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" -jscodeshift@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/jscodeshift/-/jscodeshift-0.4.1.tgz#da91a1c2eccfa03a3387a21d39948e251ced444a" - dependencies: - async "^1.5.0" - babel-plugin-transform-flow-strip-types "^6.8.0" - babel-preset-es2015 "^6.9.0" - babel-preset-stage-1 "^6.5.0" - babel-register "^6.9.0" - babylon "^6.17.3" - colors "^1.1.2" - flow-parser "^0.*" - lodash "^4.13.1" - micromatch "^2.3.7" - node-dir "0.1.8" - nomnom "^1.8.1" - recast "^0.12.5" - temp "^0.8.1" - write-file-atomic "^1.2.0" - -jscodeshift@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/jscodeshift/-/jscodeshift-0.5.0.tgz#bdb7b6cc20dd62c16aa728c3fa2d2fe66ca7c748" - dependencies: - babel-plugin-transform-flow-strip-types "^6.8.0" - babel-preset-es2015 "^6.9.0" - babel-preset-stage-1 "^6.5.0" - babel-register "^6.9.0" - babylon "^7.0.0-beta.30" - colors "^1.1.2" - flow-parser "^0.*" - lodash "^4.13.1" - micromatch "^2.3.7" - neo-async "^2.5.0" - node-dir "0.1.8" - nomnom "^1.8.1" - recast "^0.14.1" - temp "^0.8.1" - write-file-atomic "^1.2.0" - jsdom@^11.5.1: version "11.12.0" resolved "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz#1a80d40ddd378a1de59656e9e6dc5a3ba8657bc8" @@ -8895,10 +8505,6 @@ jsesc@~0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" -json-buffer@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" - json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" @@ -8990,6 +8596,10 @@ jsonify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" +jsonpack@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/jsonpack/-/jsonpack-1.1.5.tgz#d42b0dcfd91ac58ef3110f96d2c599404c3dc27c" + jsonparse@^1.2.0: version "1.3.1" resolved "http://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" @@ -9114,12 +8724,6 @@ keycode@^2.1.8, keycode@^2.1.9: version "2.2.0" resolved "https://registry.yarnpkg.com/keycode/-/keycode-2.2.0.tgz#3d0af56dc7b8b8e5cba8d0a97f107204eec22b04" -keyv@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.0.0.tgz#44923ba39e68b12a7cec7df6c3268c031f2ef373" - dependencies: - json-buffer "3.0.0" - killable@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.0.tgz#da8b84bd47de5395878f95d64d02f2449fe05e6b" @@ -9344,19 +8948,6 @@ listr-update-renderer@^0.2.0: log-update "^1.0.2" strip-ansi "^3.0.1" -listr-update-renderer@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.4.0.tgz#344d980da2ca2e8b145ba305908f32ae3f4cc8a7" - dependencies: - chalk "^1.1.3" - cli-truncate "^0.2.1" - elegant-spinner "^1.0.1" - figures "^1.7.0" - indent-string "^3.0.0" - log-symbols "^1.0.2" - log-update "^1.0.2" - strip-ansi "^3.0.1" - listr-verbose-renderer@^0.4.0: version "0.4.1" resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz#8206f4cf6d52ddc5827e5fd14989e0e965933a35" @@ -9387,28 +8978,6 @@ listr@^0.12.0: stream-to-observable "^0.1.0" strip-ansi "^3.0.1" -listr@^0.13.0: - version "0.13.0" - resolved "https://registry.yarnpkg.com/listr/-/listr-0.13.0.tgz#20bb0ba30bae660ee84cc0503df4be3d5623887d" - dependencies: - chalk "^1.1.3" - cli-truncate "^0.2.1" - figures "^1.7.0" - indent-string "^2.1.0" - is-observable "^0.2.0" - is-promise "^2.1.0" - is-stream "^1.1.0" - listr-silent-renderer "^1.1.1" - listr-update-renderer "^0.4.0" - listr-verbose-renderer "^0.4.0" - log-symbols "^1.0.2" - log-update "^1.0.2" - ora "^0.2.3" - p-map "^1.1.1" - rxjs "^5.4.2" - stream-to-observable "^0.2.0" - strip-ansi "^3.0.1" - load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" @@ -9617,7 +9186,7 @@ lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" -lodash.merge@^4.6.0: +lodash.merge@^4.6.0, lodash.merge@^4.6.1: version "4.6.1" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.1.tgz#adc25d9cb99b9391c59624f379fbba60d7111d54" @@ -9713,14 +9282,14 @@ lodash@^3.10.1, lodash@^3.3.1, lodash@^3.6.0, lodash@^3.7.0: version "3.10.1" resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" -lodash@^4.13.1, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.1: - version "4.17.5" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" - -lodash@^4.15.0: +lodash@^4.13.1, lodash@^4.15.0: version "4.17.11" resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" +lodash@^4.14.0, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.1: + version "4.17.5" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" + lodash@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/lodash/-/lodash-1.0.2.tgz#8f57560c83b59fc270bd3d561b690043430e2551" @@ -9798,10 +9367,6 @@ lower-case@^1.1.0, lower-case@^1.1.1, lower-case@^1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" -lowercase-keys@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" - lowercase-keys@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" @@ -9860,12 +9425,6 @@ make-dir@^1.0.0: dependencies: pify "^3.0.0" -make-dir@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" - dependencies: - pify "^3.0.0" - make-error@1.x: version "1.3.5" resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" @@ -9975,30 +9534,6 @@ media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" -mem-fs-editor@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/mem-fs-editor/-/mem-fs-editor-4.0.2.tgz#55a79b1e824da631254c4c95ba6366602c77af90" - dependencies: - commondir "^1.0.1" - deep-extend "^0.5.1" - ejs "^2.5.9" - glob "^7.0.3" - globby "^8.0.0" - isbinaryfile "^3.0.2" - mkdirp "^0.5.0" - multimatch "^2.0.0" - rimraf "^2.2.8" - through2 "^2.0.0" - vinyl "^2.0.1" - -mem-fs@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/mem-fs/-/mem-fs-1.1.3.tgz#b8ae8d2e3fcb6f5d3f9165c12d4551a065d989cc" - dependencies: - through2 "^2.0.0" - vinyl "^1.1.0" - vinyl-file "^2.0.0" - mem@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76" @@ -10109,7 +9644,7 @@ methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" -micromatch@^2.1.5, micromatch@^2.3.11, micromatch@^2.3.7: +micromatch@^2.1.5, micromatch@^2.3.11: version "2.3.11" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" dependencies: @@ -10422,7 +9957,7 @@ multicast-dns@^6.0.1: dns-packet "^1.3.1" thunky "^1.0.2" -multimatch@^2.0.0, multimatch@^2.1.0: +multimatch@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-2.1.0.tgz#9c7906a22fb4c02919e2f5f75161b4cdbd4b2a2b" dependencies: @@ -10587,10 +10122,6 @@ node-abi@^2.2.0: dependencies: semver "^5.4.1" -node-dir@0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.8.tgz#55fb8deb699070707fb67f91a460f0448294c77d" - node-fetch@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.1.1.tgz#369ca70b82f50c86496104a6c776d274f4e4a2d4" @@ -10728,13 +10259,6 @@ nodemon@^1.11.0: undefsafe "^2.0.2" update-notifier "^2.3.0" -nomnom@^1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/nomnom/-/nomnom-1.8.1.tgz#2151f722472ba79e50a76fc125bb8c8f2e4dc2a7" - dependencies: - chalk "~0.4.0" - underscore "~1.6.0" - nomnom@~1.6.2: version "1.6.2" resolved "https://registry.npmjs.org/nomnom/-/nomnom-1.6.2.tgz#84a66a260174408fc5b77a18f888eccc44fb6971" @@ -10795,14 +10319,6 @@ normalize-scroll-left@^0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/normalize-scroll-left/-/normalize-scroll-left-0.1.2.tgz#6b79691ba79eb5fb107fa5edfbdc06b55caee2aa" -normalize-url@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6" - dependencies: - prepend-http "^2.0.0" - query-string "^5.0.1" - sort-keys "^2.0.0" - normalize-url@^1.4.0: version "1.9.1" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" @@ -11283,20 +10799,10 @@ p-cancelable@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" -p-cancelable@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.4.1.tgz#35f363d67d52081c8d9585e37bcceb7e0bbcb2a0" - p-defer@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" -p-each-series@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-1.0.0.tgz#930f3d12dd1f50e7434457a22cd6f04ac6ad7f71" - dependencies: - p-reduce "^1.0.0" - p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" @@ -11305,10 +10811,6 @@ p-is-promise@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e" -p-lazy@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-lazy/-/p-lazy-1.0.0.tgz#ec53c802f2ee3ac28f166cc82d0b2b02de27a835" - p-limit@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.2.0.tgz#0e92b6bedcb59f022c13d0f1949dc82d15909f1c" @@ -11353,12 +10855,6 @@ p-timeout@^1.1.1: dependencies: p-finally "^1.0.0" -p-timeout@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-2.0.1.tgz#d8dd1979595d2dc0139e1fe46b8b646cb3cdf038" - dependencies: - p-finally "^1.0.0" - p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" @@ -11683,6 +11179,12 @@ polished@^1.9.3: version "1.9.3" resolved "https://registry.npmjs.org/polished/-/polished-1.9.3.tgz#d61b8a0c4624efe31e2583ff24a358932b6b75e1" +polished@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/polished/-/polished-2.2.0.tgz#5ca7e178cc5352bd7fd1efc45342f7c6d59cc982" + dependencies: + "@babel/runtime" "^7.0.0" + popper.js@1.14.3, popper.js@^1.14.1: version "1.14.3" resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.14.3.tgz#1438f98d046acf7b4d78cd502bf418ac64d4f095" @@ -12056,7 +11558,7 @@ postman-url-encoder@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/postman-url-encoder/-/postman-url-encoder-1.0.1.tgz#a094a42e9415ff0bbfdce0eaa8e6011d449ee83c" -prebuild-install@^2.2.2, prebuild-install@^2.3.0: +prebuild-install@^2.2.2: version "2.5.3" resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-2.5.3.tgz#9f65f242782d370296353710e9bc843490c19f69" dependencies: @@ -12094,15 +11596,11 @@ prepend-http@^1.0.0, prepend-http@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" -prepend-http@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" - preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" -prettier@^1.11.1, prettier@^1.5.3: +prettier@^1.11.1: version "1.12.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.12.1.tgz#c1ad20e803e7749faf905a409d2367e06bbe7325" @@ -12113,10 +11611,6 @@ pretty-bytes@^1.0.4: get-stdin "^4.0.1" meow "^3.1.0" -pretty-bytes@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-4.0.2.tgz#b2bf82e7350d65c6c33aa95aaa5a4f6327f61cd9" - pretty-format@^23.6.0: version "23.6.0" resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-23.6.0.tgz#5eaac8eeb6b33b987b7fe6097ea6a8a146ab5760" @@ -12135,19 +11629,13 @@ pretty-ms@3.1.0: parse-ms "^1.0.0" plur "^2.1.2" -prettycli@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/prettycli/-/prettycli-1.4.3.tgz#b28ec2aad9de07ae1fd75ef294fb54cbdee07ed5" - dependencies: - chalk "2.1.0" - prismjs@^1.15.0: version "1.15.0" resolved "https://registry.npmjs.org/prismjs/-/prismjs-1.15.0.tgz#8801d332e472091ba8def94976c8877ad60398d9" optionalDependencies: clipboard "^2.0.0" -private@^0.1.6, private@^0.1.7, private@^0.1.8, private@~0.1.5: +private@^0.1.6, private@^0.1.7, private@^0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" @@ -12643,7 +12131,7 @@ react-popper@^1.0.0-beta.6: typed-styles "^0.0.5" warning "^3.0.0" -react-redux@^5.0.3: +react-redux@^5.0.3, react-redux@^5.0.7: version "5.0.7" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.7.tgz#0dc1076d9afb4670f993ffaef44b8f8c1155a4c8" dependencies: @@ -12757,13 +12245,6 @@ react@^16.5.2: prop-types "^15.6.2" schedule "^0.5.0" -read-chunk@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/read-chunk/-/read-chunk-2.1.0.tgz#6a04c0928005ed9d42e1a6ac5600e19cbc7ff655" - dependencies: - pify "^3.0.0" - safe-buffer "^5.1.1" - read-cmd-shim@^1.0.1: version "1.0.1" resolved "http://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-1.0.1.tgz#2d5d157786a37c055d22077c32c53f8329e91c7b" @@ -12903,25 +12384,6 @@ realpath-native@^1.0.0: dependencies: util.promisify "^1.0.0" -recast@^0.12.5: - version "0.12.9" - resolved "https://registry.yarnpkg.com/recast/-/recast-0.12.9.tgz#e8e52bdb9691af462ccbd7c15d5a5113647a15f1" - dependencies: - ast-types "0.10.1" - core-js "^2.4.1" - esprima "~4.0.0" - private "~0.1.5" - source-map "~0.6.1" - -recast@^0.14.1: - version "0.14.7" - resolved "https://registry.yarnpkg.com/recast/-/recast-0.14.7.tgz#4f1497c2b5826d42a66e8e3c9d80c512983ff61d" - dependencies: - ast-types "0.11.3" - esprima "~4.0.0" - private "~0.1.5" - source-map "~0.6.1" - rechoir@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" @@ -13022,7 +12484,7 @@ redux-devtools-extension@^2.13.2: version "2.13.2" resolved "https://registry.yarnpkg.com/redux-devtools-extension/-/redux-devtools-extension-2.13.2.tgz#e0f9a8e8dfca7c17be92c7124958a3b94eb2911d" -redux@*: +redux@*, redux@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.0.tgz#aa698a92b729315d22b34a0553d7e6533555cc03" dependencies: @@ -13391,12 +12853,6 @@ resolve@~1.5.0: dependencies: path-parse "^1.0.5" -responselike@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" - dependencies: - lowercase-keys "^1.0.0" - restore-cursor@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" @@ -13492,7 +12948,7 @@ rsvp@^3.3.3: version "3.6.2" resolved "https://registry.npmjs.org/rsvp/-/rsvp-3.6.2.tgz#2e96491599a96cde1b515d5674a8f7a91452926a" -run-async@^2.0.0, run-async@^2.2.0: +run-async@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" dependencies: @@ -13532,7 +12988,7 @@ rxjs@^5.0.0-beta.11: dependencies: symbol-observable "1.0.1" -rxjs@^5.4.2, rxjs@^5.5.2: +rxjs@^5.5.2: version "5.5.10" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.10.tgz#fde02d7a614f6c8683d0d1957827f492e09db045" dependencies: @@ -13620,10 +13076,6 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" -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" @@ -13632,6 +13084,10 @@ scrypt-js@2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.3.tgz#bb0040be03043da9a012a2cea9fc9f852cfc87d4" +scrypt-js@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.4.tgz#32f8c5149f0797672e551c07e230f834b6af5f16" + scrypt.js@0.2.0, scrypt.js@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/scrypt.js/-/scrypt.js-0.2.0.tgz#af8d1465b71e9990110bedfc593b9479e03a8ada" @@ -13911,7 +13367,7 @@ shelljs@^0.7.3: interpret "^1.0.0" rechoir "^0.6.2" -shelljs@^0.8.0, shelljs@^0.8.2: +shelljs@^0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.2.tgz#345b7df7763f4c2340d584abb532c5f752ca9e35" dependencies: @@ -14410,12 +13866,6 @@ stream-to-observable@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/stream-to-observable/-/stream-to-observable-0.1.0.tgz#45bf1d9f2d7dc09bed81f1c307c430e68b84cffe" -stream-to-observable@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/stream-to-observable/-/stream-to-observable-0.2.0.tgz#59d6ea393d87c2c0ddac10aa0d561bc6ba6f0e10" - dependencies: - any-observable "^0.2.0" - stream-to-pull-stream@^1.7.1: version "1.7.2" resolved "https://registry.yarnpkg.com/stream-to-pull-stream/-/stream-to-pull-stream-1.7.2.tgz#757609ae1cebd33c7432d4afbe31ff78650b9dde" @@ -14448,10 +13898,6 @@ string-length@^2.0.0: astral-regex "^1.0.0" strip-ansi "^4.0.0" -string-template@~0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" - string-width@^1.0.1, string-width@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -14515,17 +13961,6 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" -strip-ansi@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-0.1.1.tgz#39e8a98d044d150660abe4a6808acf70bb7bc991" - -strip-bom-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-bom-stream/-/strip-bom-stream-2.0.0.tgz#f87db5ef2613f6968aa545abfe1ec728b6a829ca" - dependencies: - first-chunk-stream "^2.0.0" - strip-bom "^2.0.0" - strip-bom@3.0.0, strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -14619,6 +14054,20 @@ styled-components@^3.3.3: stylis-rule-sheet "^0.0.10" supports-color "^3.2.3" +styled-components@^3.4.9: + version "3.4.10" + resolved "https://registry.npmjs.org/styled-components/-/styled-components-3.4.10.tgz#9a654c50ea2b516c36ade57ddcfa296bf85c96e1" + dependencies: + buffer "^5.0.3" + css-to-react-native "^2.0.3" + fbjs "^0.8.16" + hoist-non-react-statics "^2.5.0" + prop-types "^15.5.4" + react-is "^16.3.1" + stylis "^3.5.0" + stylis-rule-sheet "^0.0.10" + supports-color "^3.2.3" + stylis-rule-sheet@^0.0.10: version "0.0.10" resolved "https://registry.npmjs.org/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz#44e64a2b076643f4b52e5ff71efc04d8c3c4a430" @@ -14649,12 +14098,6 @@ supports-color@^3.1.0, supports-color@^3.1.2, supports-color@^3.2.3: dependencies: has-flag "^1.0.0" -supports-color@^4.0.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b" - dependencies: - has-flag "^2.0.0" - supports-color@^5.5.0: version "5.5.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -14737,10 +14180,6 @@ symbol-observable@1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d" -symbol-observable@^0.2.2: - version "0.2.4" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-0.2.4.tgz#95a83db26186d6af7e7a18dbd9760a2f86d08f40" - symbol-observable@^1.0.3, symbol-observable@^1.0.4, symbol-observable@^1.1.0, symbol-observable@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" @@ -14877,7 +14316,7 @@ temp-write@^3.4.0: temp-dir "^1.0.0" uuid "^3.0.1" -temp@^0.8.1, temp@^0.8.3: +temp@^0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.3.tgz#e0c6bc4d26b903124410e4fed81103014dfc1f59" dependencies: @@ -14939,14 +14378,10 @@ text-extensions@^1.0.0: version "1.7.0" resolved "http://registry.yarnpkg.com/text-extensions/-/text-extensions-1.7.0.tgz#faaaba2625ed746d568a23e4d0aacd9bf08a8b39" -text-table@^0.2.0, text-table@~0.2.0: +text-table@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" -textextensions@2: - version "2.2.0" - resolved "https://registry.yarnpkg.com/textextensions/-/textextensions-2.2.0.tgz#38ac676151285b658654581987a0ce1a4490d286" - theming@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/theming/-/theming-1.3.0.tgz#286d5bae80be890d0adc645e5ca0498723725bdc" @@ -15318,6 +14753,10 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" +tweetnacl-util@^0.15.0: + version "0.15.0" + resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.0.tgz#4576c1cee5e2d63d207fee52f1ba02819480bc75" + tweetnacl@0.13.2: version "0.13.2" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.13.2.tgz#453161770469d45cd266c36404e2bc99a8fa9944" @@ -15326,6 +14765,10 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" +tweetnacl@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.0.tgz#713d8b818da42068740bf68386d0479e66fc8a7b" + type-check@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" @@ -15513,10 +14956,6 @@ underscore@~1.4.4: version "1.4.4" resolved "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz#61a6a32010622afa07963bf325203cf12239d604" -underscore@~1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8" - underscore@~1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.7.0.tgz#6bbaf0877500d36be34ecaa584e0db9fef035209" @@ -15631,10 +15070,6 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" -untildify@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/untildify/-/untildify-3.0.2.tgz#7f1f302055b3fea0f3e81dc78eb36766cb65e3f1" - unzip-response@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97" @@ -15684,12 +15119,6 @@ url-parse-lax@^1.0.0: dependencies: prepend-http "^1.0.1" -url-parse-lax@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" - dependencies: - prepend-http "^2.0.0" - url-parse@1.0.x: version "1.0.5" resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.0.5.tgz#0854860422afdcfefeb6c965c662d4800169927b" @@ -15813,10 +15242,6 @@ uvm@1.7.0: lodash "4.17.4" uuid "3.0.1" -v8-compile-cache@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-1.1.2.tgz#8d32e4f16974654657e676e0e467a348e89b0dc4" - v8-compile-cache@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz#a428b28bb26790734c4fc8bc9fa106fccebf6a6c" @@ -15891,17 +15316,6 @@ vfile@^2.0.0: unist-util-stringify-position "^1.0.0" vfile-message "^1.0.0" -vinyl-file@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/vinyl-file/-/vinyl-file-2.0.0.tgz#a7ebf5ffbefda1b7d18d140fcb07b223efb6751a" - dependencies: - graceful-fs "^4.1.2" - pify "^2.3.0" - pinkie-promise "^2.0.0" - strip-bom "^2.0.0" - strip-bom-stream "^2.0.0" - vinyl "^1.1.0" - vinyl-fs@^0.3.0: version "0.3.14" resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-0.3.14.tgz#9a6851ce1cac1c1cea5fe86c0931d620c2cfa9e6" @@ -15964,15 +15378,7 @@ vinyl@^0.5.0: clone-stats "^0.0.1" replace-ext "0.0.1" -vinyl@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-1.2.0.tgz#5c88036cf565e5df05558bfc911f8656df218884" - dependencies: - clone "^1.0.0" - clone-stats "^0.0.1" - replace-ext "0.0.1" - -vinyl@^2.0.0, vinyl@^2.0.1, vinyl@^2.1.0: +vinyl@^2.0.0, vinyl@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.1.0.tgz#021f9c2cf951d6b939943c89eb5ee5add4fd924c" dependencies: @@ -16365,12 +15771,6 @@ webidl-conversions@^4.0.2: version "4.0.2" resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" -webpack-addons@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/webpack-addons/-/webpack-addons-1.1.5.tgz#2b178dfe873fb6e75e40a819fa5c26e4a9bc837a" - dependencies: - jscodeshift "^0.4.0" - webpack-cli@3.1.2, webpack-cli@^3.1.1: version "3.1.2" resolved "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.1.2.tgz#17d7e01b77f89f884a2bbf9db545f0f6a648e746" @@ -16386,37 +15786,6 @@ webpack-cli@3.1.2, webpack-cli@^3.1.1: v8-compile-cache "^2.0.2" yargs "^12.0.2" -webpack-cli@^2.0.9: - version "2.1.3" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-2.1.3.tgz#65d166851abaa56067ef3f716b02a97ba6bbe84d" - dependencies: - chalk "^2.3.2" - cross-spawn "^6.0.5" - diff "^3.5.0" - enhanced-resolve "^4.0.0" - envinfo "^4.4.2" - glob-all "^3.1.0" - global-modules "^1.0.0" - got "^8.2.0" - import-local "^1.0.0" - inquirer "^5.1.0" - interpret "^1.0.4" - jscodeshift "^0.5.0" - listr "^0.13.0" - loader-utils "^1.1.0" - lodash "^4.17.5" - log-symbols "^2.2.0" - mkdirp "^0.5.1" - p-each-series "^1.0.0" - p-lazy "^1.0.0" - prettier "^1.5.3" - supports-color "^5.3.0" - v8-compile-cache "^1.1.2" - webpack-addons "^1.1.5" - yargs "^11.1.0" - yeoman-environment "^2.0.0" - yeoman-generator "^2.0.4" - webpack-dev-middleware@3.4.0: version "3.4.0" resolved "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.4.0.tgz#1132fecc9026fd90f0ecedac5cbff75d1fb45890" @@ -16678,7 +16047,7 @@ wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" -write-file-atomic@^1.1.4, write-file-atomic@^1.2.0: +write-file-atomic@^1.1.4: version "1.3.4" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-1.3.4.tgz#f807a4f0b1d9e913ae7a48112e6cc3af1991b45f" dependencies: @@ -16897,7 +16266,7 @@ yargs-parser@^9.0.2: dependencies: camelcase "^4.1.0" -yargs@11.1.0, yargs@^11.1.0: +yargs@11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.0.tgz#90b869934ed6e871115ea2ff58b03f4724ed2d77" dependencies: @@ -17049,12 +16418,6 @@ yargs@^8.0.2: y18n "^3.2.1" yargs-parser "^7.0.0" -yargs@~1.2.6: - version "1.2.6" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-1.2.6.tgz#9c7b4a82fd5d595b2bf17ab6dcc43135432fe34b" - dependencies: - minimist "^0.1.0" - yargs@~3.10.0: version "3.10.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" @@ -17071,56 +16434,6 @@ yauzl@^2.4.2: buffer-crc32 "~0.2.3" fd-slicer "~1.0.1" -yeoman-environment@^2.0.0, yeoman-environment@^2.0.5: - version "2.1.0" - resolved "https://registry.yarnpkg.com/yeoman-environment/-/yeoman-environment-2.1.0.tgz#175f49ad693aff41c8998d32f6103c20c62ec37b" - dependencies: - chalk "^2.1.0" - cross-spawn "^6.0.5" - debug "^3.1.0" - diff "^3.3.1" - escape-string-regexp "^1.0.2" - globby "^8.0.1" - grouped-queue "^0.3.3" - inquirer "^5.2.0" - is-scoped "^1.0.0" - lodash "^4.17.10" - log-symbols "^2.1.0" - mem-fs "^1.1.0" - strip-ansi "^4.0.0" - text-table "^0.2.0" - untildify "^3.0.2" - -yeoman-generator@^2.0.4: - version "2.0.5" - resolved "https://registry.yarnpkg.com/yeoman-generator/-/yeoman-generator-2.0.5.tgz#57b0b3474701293cc9ec965288f3400b00887c81" - dependencies: - async "^2.6.0" - chalk "^2.3.0" - cli-table "^0.3.1" - cross-spawn "^6.0.5" - dargs "^5.1.0" - dateformat "^3.0.3" - debug "^3.1.0" - detect-conflict "^1.0.0" - error "^7.0.2" - find-up "^2.1.0" - github-username "^4.0.0" - istextorbinary "^2.2.1" - lodash "^4.17.10" - make-dir "^1.1.0" - mem-fs-editor "^4.0.0" - minimist "^1.2.0" - pretty-bytes "^4.0.2" - read-chunk "^2.1.0" - read-pkg-up "^3.0.0" - rimraf "^2.6.2" - run-async "^2.0.0" - shelljs "^0.8.0" - text-table "^0.2.0" - through2 "^2.0.0" - yeoman-environment "^2.0.5" - yn@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a" |