diff options
author | Andrew Maurer <git@maurer.me> | 2018-10-13 23:09:50 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-10-13 23:09:50 +0800 |
commit | 69e9dbd68323439ac817e71c610dfae3716bee26 (patch) | |
tree | 6b2791f09132524963ae5e0bb58afffcb3720736 | |
parent | dc56ce454967d2bf0d5e8c93389c0fa71a9d1cc5 (diff) | |
parent | a424c2adfabbbd9313b4f5ddeeeaebd0811fd1cd (diff) | |
download | dexon-sol-tools-69e9dbd68323439ac817e71c610dfae3716bee26.tar dexon-sol-tools-69e9dbd68323439ac817e71c610dfae3716bee26.tar.gz dexon-sol-tools-69e9dbd68323439ac817e71c610dfae3716bee26.tar.bz2 dexon-sol-tools-69e9dbd68323439ac817e71c610dfae3716bee26.tar.lz dexon-sol-tools-69e9dbd68323439ac817e71c610dfae3716bee26.tar.xz dexon-sol-tools-69e9dbd68323439ac817e71c610dfae3716bee26.tar.zst dexon-sol-tools-69e9dbd68323439ac817e71c610dfae3716bee26.zip |
Merge branch 'development' into development
139 files changed, 3898 insertions, 1396 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml index 31954c608..876b861d3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -23,6 +23,7 @@ jobs: paths: - ~/repo build-website: + resource_class: medium+ docker: - image: circleci/node:9 working_directory: ~/repo @@ -161,6 +162,90 @@ jobs: key: coverage-web3-wrapper-{{ .Environment.CIRCLE_SHA1 }} paths: - ~/repo/packages/web3-wrapper/coverage/lcov.info + test-python: + working_directory: ~/repo + docker: + - image: circleci/python + steps: + - checkout + - run: sudo chown -R circleci:circleci /usr/local/bin + - run: sudo chown -R circleci:circleci /usr/local/lib/python3.7/site-packages + - restore_cache: + key: deps9-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }} + - run: + command: | + cd python-packages/order_utils + python -m ensurepip + python -m pip install -e .[dev] + - save_cache: + key: deps9-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }} + paths: + - "/usr/local/bin" + - "/usr/local/lib/python3.7/site-packages" + - ".eggs" + - ".mypy_cache" + - ".pytest_cache" + - ".tox" + - run: + command: | + cd python-packages/order_utils + coverage run setup.py test + - save_cache: + key: coverage-python-order-utils-{{ .Environment.CIRCLE_SHA1 }} + paths: + - ~/repo/python-packages/order_utils/.coverage + test-rest-python: + working_directory: ~/repo + docker: + - image: circleci/python + steps: + - checkout + - run: sudo chown -R circleci:circleci /usr/local/bin + - run: sudo chown -R circleci:circleci /usr/local/lib/python3.7/site-packages + - restore_cache: + key: deps9-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }} + - run: + command: | + cd python-packages/order_utils + python -m ensurepip + python -m pip install -e .[dev] + - save_cache: + key: deps9-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }} + paths: + - "/usr/local/bin" + - "/usr/local/lib/python3.7/site-packages" + - ".eggs" + - ".mypy_cache" + - ".pytest_cache" + - ".tox" + - run: + command: | + cd python-packages/order_utils + tox + static-tests-python: + working_directory: ~/repo + docker: + - image: circleci/python + steps: + - checkout + - run: sudo chown -R circleci:circleci /usr/local/bin + - run: sudo chown -R circleci:circleci /usr/local/lib/python3.7/site-packages + - restore_cache: + key: deps9-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }} + - run: + command: | + cd python-packages/order_utils + python -m ensurepip + python -m pip install -e .[dev] + - save_cache: + key: deps9-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }} + paths: + - "/usr/local/bin" + - "/usr/local/lib/python3.7/site-packages" + - run: + command: | + cd python-packages/order_utils + python setup.py lint static-tests: working_directory: ~/repo docker: @@ -172,7 +257,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 @@ -232,6 +317,9 @@ jobs: - restore_cache: keys: - coverage-contracts-{{ .Environment.CIRCLE_SHA1 }} + - restore_cache: + keys: + - coverage-python-order-utils-{{ .Environment.CIRCLE_SHA1 }} - run: yarn report_coverage workflows: version: 2 @@ -262,3 +350,8 @@ workflows: - submit-coverage: requires: - test-rest + - test-python + - test-python + - static-tests-python + # skip python tox run for now, as we don't yet have multiple test environments to support. + #- test-rest-python diff --git a/.gitignore b/.gitignore index dd022825d..6143dedb0 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 @@ -108,3 +109,12 @@ packages/sol-compiler/solc_bin/ # Monorepo scripts packages/*/scripts/ + +# python stuff +.eggs +.mypy_cache +.tox +python-packages/*/build +__pycache__ +python-packages/*/src/*.egg-info +python-packages/*/.coverage diff --git a/package.json b/package.json index 8eb736cc4..e6ae0ebde 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "ganache": "ganache-cli -p 8545 --networkId 50 -m \"${npm_package_config_mnemonic}\"", "prettier": "prettier --write '**/*.{ts,tsx,json,md}' --config .prettierrc", "prettier:ci": "prettier --list-different '**/*.{ts,tsx,json,md}' --config .prettierrc", - "report_coverage": "lcov-result-merger 'packages/*/coverage/lcov.info' | coveralls", + "report_coverage": "lcov-result-merger './{packages/*/coverage/lcov.info,python-packages/*/.coverage}' | coveralls", "test:installation": "node ./packages/monorepo-scripts/lib/test_installation.js", "test:installation:local": "IS_LOCAL_PUBLISH=true node ./packages/monorepo-scripts/lib/test_installation.js", "test:publish:circleci:comment": "HACK(albrow) We need an automated way to login to npm and echo+sleep piped to stdin was the only way I could find to do it.", @@ -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 4a1b5012b..8280c73a4 100644 --- a/packages/order-watcher/src/index.ts +++ b/packages/order-watcher/src/index.ts @@ -13,4 +13,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 6f539fc1f..d33048b61 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', @@ -600,6 +599,28 @@ export interface Metadata { 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; +} + export interface Stats { orderCount: number; } 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/md/docs/json_schemas/1.0.0/introduction.md b/packages/website/md/docs/json_schemas/1.0.0/introduction.md index a27f4b521..5b2e90387 100644 --- a/packages/website/md/docs/json_schemas/1.0.0/introduction.md +++ b/packages/website/md/docs/json_schemas/1.0.0/introduction.md @@ -1,3 +1,3 @@ Welcome to the [@0xproject/json-schemas](https://github.com/0xProject/0x-monorepo/tree/development/packages/json-schemas) documentation! This package provides JSON schemas for validating 0x Protocol & Standard Relayer API data structures. It provides both the raw JSON schemas and a schema validator class to interact with them from a JS project. -If you are not using a Javascript-based language for your project, you can copy-paste the JSON schemas within this package and use them together with a [JSON Schema](http://json-schema.org/) implementation in your [language of choice](http://json-schema.org/implementations.html) (e.g Python, Haskell, Go, C, C++, Rust, Ruby, Scala, etc...). +If you are not using a Javascript-based language for your project, you can use a Javascript environment to render the JSON schemas within this package and use them together with a [JSON Schema](http://json-schema.org/) implementation in your [language of choice](http://json-schema.org/implementations.html) (e.g Python, Haskell, Go, C, C++, Rust, Ruby, Scala, etc...). All the schema files are currently TypeScript that require evaluation in order to be recognized as valid JSON. diff --git a/packages/website/package.json b/packages/website/package.json index 82109d95f..8f69460d6 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -59,6 +59,7 @@ "react-typist": "^2.0.4", "redux": "^3.6.0", "redux-devtools-extension": "^2.13.2", + "rollbar": "^2.4.7", "semver-sort": "0.0.4", "styled-components": "^3.3.0", "thenby": "^1.2.3", diff --git a/packages/website/public/images/team/steve.png b/packages/website/public/images/team/steve.png Binary files differnew file mode 100644 index 000000000..751583fba --- /dev/null +++ b/packages/website/public/images/team/steve.png 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; } diff --git a/packages/website/ts/pages/about/about.tsx b/packages/website/ts/pages/about/about.tsx index 037647161..2629f8632 100644 --- a/packages/website/ts/pages/about/about.tsx +++ b/packages/website/ts/pages/about/about.tsx @@ -240,6 +240,16 @@ const teamRow8: ProfileInfo[] = [ }, ]; +const teamRow9: ProfileInfo[] = [ + { + name: 'Steve Klebanoff', + title: 'Senior Engineer', + description: ` Full-stack engineer. Previously Staff Software Engineer at Appfolio. Computer Science & Cognitive Psychology at Northeastern University.`, + image: 'images/team/steve.png', + linkedIn: 'https://www.linkedin.com/in/steveklebanoff/', + }, +]; + const advisors1: ProfileInfo[] = [ { name: 'Fred Ehrsam', @@ -348,6 +358,7 @@ export class About extends React.Component<AboutProps, AboutState> { <div className="clearfix">{this._renderProfiles(teamRow6)}</div> <div className="clearfix">{this._renderProfiles(teamRow7)}</div> <div className="clearfix">{this._renderProfiles(teamRow8)}</div> + <div className="clearfix">{this._renderProfiles(teamRow9)}</div> </div> <div className="pt3 pb2"> <div diff --git a/packages/website/ts/utils/error_reporter.ts b/packages/website/ts/utils/error_reporter.ts index 6008fffed..bec92e829 100644 --- a/packages/website/ts/utils/error_reporter.ts +++ b/packages/website/ts/utils/error_reporter.ts @@ -1,4 +1,5 @@ import { logUtils } from '@0xproject/utils'; +import Rollbar = require('rollbar'); import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; import { utils } from 'ts/utils/utils'; @@ -36,8 +37,8 @@ const rollbarConfig = { 'SecurityError (DOM Exception 18)', ], }; -import Rollbar = require('../../public/js/rollbar.umd.min.js'); -const rollbar = Rollbar.init(rollbarConfig); + +const rollbar = new Rollbar(rollbarConfig); export const errorReporter = { report(err: Error): void { diff --git a/python-packages/order_utils/package.json b/python-packages/order_utils/package.json new file mode 100644 index 000000000..125ca71e0 --- /dev/null +++ b/python-packages/order_utils/package.json @@ -0,0 +1,17 @@ +{ + "comment": "this file exists as an entry point to building this project, specifically for humans that are familiar with yarn and already have it installed. this file is not used in any automation or CI.", + "scripts": { + "install": "pip install -e .[dev]", + "build": "python setup.py build && yarn build:docs", + "build:docs": "python setup.py build_sphinx && sphinx-apidoc -o build/docs/api src", + "test:comment": "test in local environment. to test in all environments, use test:all", + "test": "python setup.py test", + "test:all": "tox", + "test:coverage": "coverage run setup.py test && coveralls", + "lint": "python setup.py lint", + "clean": "python setup.py clean" + }, + "dependencies:comment": "managed in setup.py", + "devDependencies:comment": "managed in setup.py", + "license": "Apache-2.0" +} diff --git a/python-packages/order_utils/setup.py b/python-packages/order_utils/setup.py new file mode 100644 index 000000000..a76d724aa --- /dev/null +++ b/python-packages/order_utils/setup.py @@ -0,0 +1,128 @@ +"""setuptools module for order_utils package.""" + +import subprocess # nosec +from shutil import rmtree +from os import path, remove, walk + +from distutils.command.clean import clean # type: ignore +from setuptools import setup # type: ignore +import setuptools.command.build_py # type: ignore +from setuptools.command.test import test as TestCommand # type: ignore + + +class TestCommandExtension(TestCommand): + """Run pytest tests.""" + + def run_tests(self): + """Invoke pytest.""" + import pytest # type: ignore + + pytest.main() + + +# pylint: disable=too-many-ancestors +class LintCommand(setuptools.command.build_py.build_py): + """Custom setuptools command class for running linters.""" + + def run(self): + """Run linter shell commands.""" + lint_commands = [ + # formatter: + "black --line-length 79 --check --diff src test setup.py".split(), + # style guide checker (formerly pep8): + "pycodestyle src test setup.py".split(), + # docstring style checker: + "pydocstyle src test setup.py".split(), + # static type checker: + "mypy src setup.py".split(), + # security issue checker: + "bandit -r src ./setup.py".split(), + # general linter: + "pylint src test setup.py".split(), + # pylint takes relatively long to run, so it runs last, to enable + # fast failures. + ] + for lint_command in lint_commands: + print( + "Running lint command `", " ".join(lint_command).strip(), "`" + ) + subprocess.check_call(lint_command) # nosec + + +class CleanCommandExtension(clean): + """Custom command to do custom cleanup.""" + + def run(self): + """Run the regular clean, followed by our custom commands.""" + super().run() + rmtree("build", ignore_errors=True) + rmtree(".mypy_cache", ignore_errors=True) + rmtree(".tox", ignore_errors=True) + rmtree(".pytest_cache", ignore_errors=True) + rmtree("src/order_utils.egg-info", ignore_errors=True) + # delete all .pyc files + for root, _, files in walk("."): + for file in files: + (_, extension) = path.splitext(file) + if extension == ".pyc": + remove(path.join(root, file)) + + +setup( + name="order_utils", + version="1.0.0", + description="Order utilities for 0x applications", + author="F. Eugene Aumson", + cmdclass={ + "clean": CleanCommandExtension, + "lint": LintCommand, + "test": TestCommandExtension, + }, + include_package_data=True, + install_requires=["web3"], + extras_require={ + "dev": [ + "bandit", + "black", + "coverage", + "coveralls", + "mypy", + "pycodestyle", + "pydocstyle", + "pylint", + "pytest", + "sphinx", + "tox", + ] + }, + python_requires=">=3.6, <4", + package_data={"zero_ex.order_utils": ["py.typed"]}, + package_dir={"": "src"}, + license="Apache 2.0", + keywords=( + "ethereum cryptocurrency 0x decentralized blockchain dex exchange" + ), + packages=["zero_ex.order_utils"], + classifiers=[ + "Development Status :: 1 - Planning", + "Intended Audience :: Developers", + "Intended Audience :: Financial and Insurance Industry", + "License :: OSI Approved :: Apache Software License", + "Natural Language :: English", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Topic :: Office/Business :: Financial", + "Topic :: Software Development :: Libraries", + "Topic :: Utilities", + ], + zip_safe=False, + command_options={ + "build_sphinx": { + "source_dir": ("setup.py", "src"), + "build_dir": ("setup.py", "build/docs"), + } + }, +) diff --git a/python-packages/order_utils/src/conf.py b/python-packages/order_utils/src/conf.py new file mode 100644 index 000000000..f3f15967c --- /dev/null +++ b/python-packages/order_utils/src/conf.py @@ -0,0 +1,50 @@ +"""Configuration file for the Sphinx documentation builder.""" + +# Reference: http://www.sphinx-doc.org/en/master/config + +# pylint: disable=invalid-name +# because these variables are not named in upper case, as globals should be. + +project = "order_utils.py" +# pylint: disable=redefined-builtin +copyright = "2018, ZeroEx, Intl." +author = "F. Eugene Aumson" +version = "" # The short X.Y version +release = "" # The full version, including alpha/beta/rc tags + +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.doctest", + "sphinx.ext.intersphinx", + "sphinx.ext.coverage", + "sphinx.ext.viewcode", +] + +templates_path = ["doc_templates"] + +source_suffix = ".rst" +# eg: source_suffix = [".rst", ".md"] + +master_doc = "index" # The master toctree document. + +language = None + +exclude_patterns = [] # type: ignore + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = None + +html_theme = "alabaster" + +html_static_path = ["doc_static"] +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". + +# Output file base name for HTML help builder. +htmlhelp_basename = "order_utilspydoc" + +# -- Extension configuration: + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = {"https://docs.python.org/": None} diff --git a/python-packages/order_utils/src/index.rst b/python-packages/order_utils/src/index.rst new file mode 100644 index 000000000..cbc4c8409 --- /dev/null +++ b/python-packages/order_utils/src/index.rst @@ -0,0 +1,22 @@ +.. source for the sphinx-generated build/docs/web/index.html + +order_utils.py +============== + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + +.. automodule:: zero_ex.order_utils + :members: + +.. automodule:: zero_ex.order_utils.signature_utils + :members: + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/python-packages/order_utils/src/zero_ex/__init__.py b/python-packages/order_utils/src/zero_ex/__init__.py new file mode 100644 index 000000000..c3ed1562a --- /dev/null +++ b/python-packages/order_utils/src/zero_ex/__init__.py @@ -0,0 +1 @@ +"""0x Python API.""" diff --git a/python-packages/order_utils/src/zero_ex/order_utils/__init__.py b/python-packages/order_utils/src/zero_ex/order_utils/__init__.py new file mode 100644 index 000000000..f014af0f6 --- /dev/null +++ b/python-packages/order_utils/src/zero_ex/order_utils/__init__.py @@ -0,0 +1 @@ +"""Order utilities for 0x applications.""" diff --git a/python-packages/order_utils/src/zero_ex/order_utils/signature_utils.py b/python-packages/order_utils/src/zero_ex/order_utils/signature_utils.py new file mode 100644 index 000000000..7f4697106 --- /dev/null +++ b/python-packages/order_utils/src/zero_ex/order_utils/signature_utils.py @@ -0,0 +1,13 @@ +"""Signature utilities.""" + + +def ec_sign_order_hash(): + """Signs an orderHash. + + Returns its elliptic curve signature and signature type. This method + currently supports TestRPC, Geth, and Parity above and below v1.6.6. + + >>> ec_sign_order_hash() + 'stub return value' + """ + return "stub return value" diff --git a/python-packages/order_utils/src/zero_ex/py.typed b/python-packages/order_utils/src/zero_ex/py.typed new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python-packages/order_utils/src/zero_ex/py.typed diff --git a/python-packages/order_utils/test/__init__.py b/python-packages/order_utils/test/__init__.py new file mode 100644 index 000000000..ec5b114aa --- /dev/null +++ b/python-packages/order_utils/test/__init__.py @@ -0,0 +1 @@ +"""Tests of zero_x.order_utils.""" diff --git a/python-packages/order_utils/test/test_doctest.py b/python-packages/order_utils/test/test_doctest.py new file mode 100644 index 000000000..a0e61f84a --- /dev/null +++ b/python-packages/order_utils/test/test_doctest.py @@ -0,0 +1,10 @@ +"""Exercise doctests for order_utils module.""" + +from doctest import testmod +from zero_ex.order_utils import signature_utils + + +def test_doctest(): + """Invoke doctest on the module.""" + (failure_count, _) = testmod(signature_utils) + assert failure_count == 0 diff --git a/python-packages/order_utils/test/test_signature_utils.py b/python-packages/order_utils/test/test_signature_utils.py new file mode 100644 index 000000000..7e830f9f8 --- /dev/null +++ b/python-packages/order_utils/test/test_signature_utils.py @@ -0,0 +1,8 @@ +"""Tests of 0x.order_utils.signature_utils.*.""" + +from zero_ex.order_utils.signature_utils import ec_sign_order_hash + + +def test_ec_sign_order_hash(): + """Test the signing of order hashes.""" + assert ec_sign_order_hash() == "stub return value" diff --git a/python-packages/order_utils/tox.ini b/python-packages/order_utils/tox.ini new file mode 100644 index 000000000..1cce32b5f --- /dev/null +++ b/python-packages/order_utils/tox.ini @@ -0,0 +1,12 @@ +# tox (https://tox.readthedocs.io/) is a tool for running tests +# in multiple virtualenvs. This configuration file will run the +# test suite on all supported python versions. To use it, "pip install tox" +# and then run "tox" from this directory. + +[tox] +envlist = py37 + +[testenv] +commands = + pip install -e .[dev] + python setup.py test @@ -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" @@ -4110,6 +3867,10 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" +console-polyfill@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/console-polyfill/-/console-polyfill-0.3.0.tgz#84900902a18c47a5eba932be75fa44d23e8af861" + constant-case@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-2.0.0.tgz#4175764d389d3fa9c8ecd29186ed6005243b6a46" @@ -4274,10 +4035,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 +4350,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 +4376,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 +4523,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 +4682,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 +4696,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 +4719,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 +4918,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 +4926,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 +5010,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,12 +5066,11 @@ 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" +error-stack-parser@1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-1.3.3.tgz#fada6e3a9cd2b0e080e6d6fc751418649734f35c" dependencies: - string-template "~0.2.1" - xtend "~4.0.0" + stackframe "^0.3.1" es-abstract@^1.10.0, es-abstract@^1.5.1, es-abstract@^1.6.1: version "1.12.0" @@ -5515,7 +5243,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 +5382,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 +5430,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 +5487,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 +5594,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 +5604,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 +6137,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 +6154,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 +6161,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 +6238,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 +6364,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 +6407,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 +6510,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 +6517,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 +6531,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 +6571,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 +6581,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 +6745,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 +6777,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 +6812,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 +6830,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 +6949,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 +7185,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 +7344,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 +7477,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 +7513,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 +7806,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 +7870,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" @@ -8274,6 +7946,10 @@ is-wsl@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" +is_js@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/is_js/-/is_js-0.9.0.tgz#0ab94540502ba7afa24c856aa985561669e9c52d" + isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -8286,10 +7962,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 +8116,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 +8480,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 +8519,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 +8610,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 +8738,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 +8962,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 +8992,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 +9200,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 +9296,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 +9381,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 +9439,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 +9548,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 +9658,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 +9971,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 +10136,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 +10273,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 +10333,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 +10813,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 +10825,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 +10869,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 +11193,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 +11572,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 +11610,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 +11625,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 +11643,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 +12145,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 +12259,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 +12398,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 +12498,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: @@ -13179,6 +12655,12 @@ request-ip@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/request-ip/-/request-ip-1.2.3.tgz#66988f0e22406ec4af630d19b573fe4b447c3b49" +request-ip@~2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/request-ip/-/request-ip-2.0.2.tgz#deeae6d4af21768497db8cd05fa37143f8f1257e" + dependencies: + is_js "^0.9.0" + request-promise-core@1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz#3eee00b2c5aa83239cfb04c5700da36f81cd08b6" @@ -13391,12 +12873,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" @@ -13481,6 +12957,21 @@ rollbar@^0.6.5: optionalDependencies: decache "^3.0.5" +rollbar@^2.4.7: + version "2.4.7" + resolved "https://registry.yarnpkg.com/rollbar/-/rollbar-2.4.7.tgz#9b1de1a0fab6b6e63fcfcd322c26081a1d8242e8" + dependencies: + async "~1.2.1" + console-polyfill "0.3.0" + debug "2.6.9" + error-stack-parser "1.3.3" + json-stringify-safe "~5.0.0" + lru-cache "~2.2.1" + request-ip "~2.0.1" + uuid "3.0.x" + optionalDependencies: + decache "^3.0.5" + rst-selector-parser@^2.2.3: version "2.2.3" resolved "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz#81b230ea2fcc6066c89e3472de794285d9b03d91" @@ -13492,7 +12983,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 +13023,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 +13111,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 +13119,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 +13402,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: @@ -14335,6 +13826,10 @@ stack-utils@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.1.tgz#d4f33ab54e8e38778b0ca5cfd3b3afb12db68620" +stackframe@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-0.3.1.tgz#33aa84f1177a5548c8935533cbfeb3420975f5a4" + state-toggle@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.0.tgz#d20f9a616bb4f0c3b98b91922d25b640aa2bc425" @@ -14410,12 +13905,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 +13937,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 +14000,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 +14093,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 +14137,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 +14219,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 +14355,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 +14417,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 +14792,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 +14804,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 +14995,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 +15109,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 +15158,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" @@ -15784,7 +15252,7 @@ uuid@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.1.tgz#c2a30dedb3e535d72ccf82e343941a50ba8533ac" -uuid@3.0.1, uuid@~3.0.0: +uuid@3.0.1, uuid@3.0.x, uuid@~3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" @@ -15813,10 +15281,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 +15355,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 +15417,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 +15810,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 +15825,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 +16086,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 +16305,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 +16457,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 +16473,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" |