diff options
184 files changed, 2416 insertions, 642 deletions
diff --git a/.prettierignore b/.prettierignore index 7ef0f6735..db389bdb9 100644 --- a/.prettierignore +++ b/.prettierignore @@ -6,6 +6,7 @@ lib /packages/contract-artifacts/artifacts /python-packages/order_utils/src/zero_ex/contract_artifacts/artifacts /packages/json-schemas/schemas +/python-packages/order_utils/src/zero_ex/json_schemas/schemas /packages/metacoin/src/contract_wrappers /packages/metacoin/artifacts /packages/sra-spec/public/ diff --git a/package.json b/package.json index e598ac2d3..96c18a2c8 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "packages/*" ], "scripts": { - "ganache": "ganache-cli -p 8545 --networkId 50 -m \"${npm_package_config_mnemonic}\"", + "ganache": "ganache-cli -p 8545 --gasLimit 10000000 --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,python-packages/*/.coverage}' | coveralls", diff --git a/packages/0x.js/CHANGELOG.json b/packages/0x.js/CHANGELOG.json index c5c45026b..0321ee200 100644 --- a/packages/0x.js/CHANGELOG.json +++ b/packages/0x.js/CHANGELOG.json @@ -1,5 +1,23 @@ [ { + "timestamp": 1542208198, + "version": "2.0.4", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { + "timestamp": 1542134075, + "version": "2.0.3", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "timestamp": 1542028948, "version": "2.0.2", "changes": [ diff --git a/packages/0x.js/CHANGELOG.md b/packages/0x.js/CHANGELOG.md index 3261e4004..2467311bf 100644 --- a/packages/0x.js/CHANGELOG.md +++ b/packages/0x.js/CHANGELOG.md @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.0.4 - _November 14, 2018_ + + * Dependencies updated + +## v2.0.3 - _November 13, 2018_ + + * Dependencies updated + ## v2.0.2 - _November 12, 2018_ * Dependencies updated diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json index d49c8f188..755ff7512 100644 --- a/packages/0x.js/package.json +++ b/packages/0x.js/package.json @@ -1,6 +1,6 @@ { "name": "0x.js", - "version": "2.0.2", + "version": "2.0.4", "engines": { "node": ">=6.12" }, @@ -42,11 +42,11 @@ }, "license": "Apache-2.0", "devDependencies": { - "@0x/abi-gen": "^1.0.15", - "@0x/abi-gen-wrappers": "^1.0.3", + "@0x/abi-gen": "^1.0.16", + "@0x/abi-gen-wrappers": "^1.0.5", "@0x/contract-addresses": "^1.1.0", - "@0x/dev-utils": "^1.0.15", - "@0x/migrations": "^2.0.2", + "@0x/dev-utils": "^1.0.17", + "@0x/migrations": "^2.0.4", "@0x/tslint-config": "^1.0.10", "@types/lodash": "4.14.104", "@types/mocha": "^2.2.42", @@ -73,16 +73,16 @@ "webpack": "^4.20.2" }, "dependencies": { - "@0x/assert": "^1.0.16", - "@0x/base-contract": "^3.0.4", - "@0x/contract-wrappers": "^4.0.0", - "@0x/order-utils": "^3.0.0", - "@0x/order-watcher": "^2.2.2", - "@0x/subproviders": "^2.1.2", + "@0x/assert": "^1.0.17", + "@0x/base-contract": "^3.0.6", + "@0x/contract-wrappers": "^4.0.2", + "@0x/order-utils": "^3.0.2", + "@0x/order-watcher": "^2.2.4", + "@0x/subproviders": "^2.1.4", "@0x/types": "^1.2.1", "@0x/typescript-typings": "^3.0.4", - "@0x/utils": "^2.0.4", - "@0x/web3-wrapper": "^3.1.2", + "@0x/utils": "^2.0.5", + "@0x/web3-wrapper": "^3.1.4", "@types/web3-provider-engine": "^14.0.0", "ethereum-types": "^1.1.2", "ethers": "~4.0.4", diff --git a/packages/abi-gen-wrappers/CHANGELOG.json b/packages/abi-gen-wrappers/CHANGELOG.json index 14a0eadcb..fff2bc41e 100644 --- a/packages/abi-gen-wrappers/CHANGELOG.json +++ b/packages/abi-gen-wrappers/CHANGELOG.json @@ -1,5 +1,23 @@ [ { + "timestamp": 1542208198, + "version": "1.0.5", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { + "timestamp": 1542134075, + "version": "1.0.4", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "timestamp": 1542028948, "version": "1.0.3", "changes": [ diff --git a/packages/abi-gen-wrappers/CHANGELOG.md b/packages/abi-gen-wrappers/CHANGELOG.md index 649540841..4dea965f1 100644 --- a/packages/abi-gen-wrappers/CHANGELOG.md +++ b/packages/abi-gen-wrappers/CHANGELOG.md @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.5 - _November 14, 2018_ + + * Dependencies updated + +## v1.0.4 - _November 13, 2018_ + + * Dependencies updated + ## v1.0.3 - _November 12, 2018_ * Dependencies updated diff --git a/packages/abi-gen-wrappers/package.json b/packages/abi-gen-wrappers/package.json index 5dab1fd3c..7236cf6ff 100644 --- a/packages/abi-gen-wrappers/package.json +++ b/packages/abi-gen-wrappers/package.json @@ -1,6 +1,6 @@ { "name": "@0x/abi-gen-wrappers", - "version": "1.0.3", + "version": "1.0.5", "engines": { "node": ">=6.12" }, @@ -30,17 +30,17 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/abi-gen-wrappers/README.md", "devDependencies": { - "@0x/abi-gen": "^1.0.15", + "@0x/abi-gen": "^1.0.16", "@0x/tslint-config": "^1.0.10", - "@0x/utils": "^2.0.4", - "@0x/web3-wrapper": "^3.1.2", + "@0x/utils": "^2.0.5", + "@0x/web3-wrapper": "^3.1.4", "ethereum-types": "^1.1.2", "ethers": "~4.0.4", "lodash": "^4.17.5", "shx": "^0.2.2" }, "dependencies": { - "@0x/base-contract": "^3.0.4" + "@0x/base-contract": "^3.0.6" }, "publishConfig": { "access": "public" diff --git a/packages/abi-gen/CHANGELOG.json b/packages/abi-gen/CHANGELOG.json index 658a997f4..1735c6b53 100644 --- a/packages/abi-gen/CHANGELOG.json +++ b/packages/abi-gen/CHANGELOG.json @@ -1,5 +1,14 @@ [ { + "timestamp": 1542208198, + "version": "1.0.16", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "version": "1.0.15", "changes": [ { diff --git a/packages/abi-gen/CHANGELOG.md b/packages/abi-gen/CHANGELOG.md index 21fda1656..23e2f8914 100644 --- a/packages/abi-gen/CHANGELOG.md +++ b/packages/abi-gen/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.16 - _November 14, 2018_ + + * Dependencies updated + ## v1.0.15 - _November 9, 2018_ * Dependencies updated diff --git a/packages/abi-gen/package.json b/packages/abi-gen/package.json index 5367c9c29..f69d24116 100644 --- a/packages/abi-gen/package.json +++ b/packages/abi-gen/package.json @@ -1,6 +1,6 @@ { "name": "@0x/abi-gen", - "version": "1.0.15", + "version": "1.0.16", "engines": { "node": ">=6.12" }, @@ -32,7 +32,7 @@ "homepage": "https://github.com/0xProject/0x-monorepo/packages/abi-gen/README.md", "dependencies": { "@0x/typescript-typings": "^3.0.4", - "@0x/utils": "^2.0.4", + "@0x/utils": "^2.0.5", "chalk": "^2.3.0", "ethereum-types": "^1.1.2", "glob": "^7.1.2", diff --git a/packages/assert/CHANGELOG.json b/packages/assert/CHANGELOG.json index 67c14f68f..2b4a3e7a1 100644 --- a/packages/assert/CHANGELOG.json +++ b/packages/assert/CHANGELOG.json @@ -1,5 +1,14 @@ [ { + "timestamp": 1542208198, + "version": "1.0.17", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "timestamp": 1542028948, "version": "1.0.16", "changes": [ diff --git a/packages/assert/CHANGELOG.md b/packages/assert/CHANGELOG.md index 01b1dbbb5..66f0971c1 100644 --- a/packages/assert/CHANGELOG.md +++ b/packages/assert/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.17 - _November 14, 2018_ + + * Dependencies updated + ## v1.0.16 - _November 12, 2018_ * Dependencies updated diff --git a/packages/assert/package.json b/packages/assert/package.json index 1db886737..ec093a5be 100644 --- a/packages/assert/package.json +++ b/packages/assert/package.json @@ -1,6 +1,6 @@ { "name": "@0x/assert", - "version": "1.0.16", + "version": "1.0.17", "engines": { "node": ">=6.12" }, @@ -44,9 +44,9 @@ "typescript": "3.0.1" }, "dependencies": { - "@0x/json-schemas": "^2.1.0", + "@0x/json-schemas": "^2.1.1", "@0x/typescript-typings": "^3.0.4", - "@0x/utils": "^2.0.4", + "@0x/utils": "^2.0.5", "lodash": "^4.17.5", "valid-url": "^1.0.9" }, diff --git a/packages/asset-buyer/CHANGELOG.json b/packages/asset-buyer/CHANGELOG.json index aa0d28879..0e4623d05 100644 --- a/packages/asset-buyer/CHANGELOG.json +++ b/packages/asset-buyer/CHANGELOG.json @@ -1,5 +1,24 @@ [ { + "version": "3.0.0", + "changes": [ + { + "note": "update `getBuyQuoteAsync` to return eth spent on assets instead of per unit amount", + "pr": 1252 + } + ], + "timestamp": 1542208198 + }, + { + "timestamp": 1542134075, + "version": "2.2.2", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "timestamp": 1542028948, "version": "2.2.1", "changes": [ diff --git a/packages/asset-buyer/CHANGELOG.md b/packages/asset-buyer/CHANGELOG.md index 331e4df9d..d6013e53e 100644 --- a/packages/asset-buyer/CHANGELOG.md +++ b/packages/asset-buyer/CHANGELOG.md @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v3.0.0 - _November 14, 2018_ + + * update `getBuyQuoteAsync` to return eth spent on assets instead of per unit amount (#1252) + +## v2.2.2 - _November 13, 2018_ + + * Dependencies updated + ## v2.2.1 - _November 12, 2018_ * Dependencies updated diff --git a/packages/asset-buyer/package.json b/packages/asset-buyer/package.json index 58daac16e..fad33476b 100644 --- a/packages/asset-buyer/package.json +++ b/packages/asset-buyer/package.json @@ -1,6 +1,6 @@ { "name": "@0x/asset-buyer", - "version": "2.2.1", + "version": "3.0.0", "engines": { "node": ">=6.12" }, @@ -36,16 +36,16 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/asset-buyer/README.md", "dependencies": { - "@0x/assert": "^1.0.16", - "@0x/connect": "^3.0.4", - "@0x/contract-wrappers": "^4.0.0", - "@0x/json-schemas": "^2.1.0", - "@0x/order-utils": "^3.0.0", - "@0x/subproviders": "^2.1.2", + "@0x/assert": "^1.0.17", + "@0x/connect": "^3.0.6", + "@0x/contract-wrappers": "^4.0.2", + "@0x/json-schemas": "^2.1.1", + "@0x/order-utils": "^3.0.2", + "@0x/subproviders": "^2.1.4", "@0x/types": "^1.2.1", "@0x/typescript-typings": "^3.0.4", - "@0x/utils": "^2.0.4", - "@0x/web3-wrapper": "^3.1.2", + "@0x/utils": "^2.0.5", + "@0x/web3-wrapper": "^3.1.4", "ethereum-types": "^1.1.2", "lodash": "^4.17.10" }, diff --git a/packages/asset-buyer/src/types.ts b/packages/asset-buyer/src/types.ts index 3f1e6ff21..3b573edca 100644 --- a/packages/asset-buyer/src/types.ts +++ b/packages/asset-buyer/src/types.ts @@ -54,12 +54,12 @@ export interface BuyQuote { } /** - * ethPerAssetPrice: The price of one unit of the desired asset in ETH + * assetEthAmount: The amount of eth required to pay for the requested asset. * feeEthAmount: The amount of eth required to pay the affiliate fee. - * totalEthAmount: the total amount of eth required to complete the buy. (Filling orders, feeOrders, and paying affiliate fee) + * totalEthAmount: The total amount of eth required to complete the buy (filling orders, feeOrders, and paying affiliate fee). */ export interface BuyQuoteInfo { - ethPerAssetPrice: BigNumber; + assetEthAmount: BigNumber; feeEthAmount: BigNumber; totalEthAmount: BigNumber; } diff --git a/packages/asset-buyer/src/utils/assert.ts b/packages/asset-buyer/src/utils/assert.ts index 2466f53a4..fcf9b0d0e 100644 --- a/packages/asset-buyer/src/utils/assert.ts +++ b/packages/asset-buyer/src/utils/assert.ts @@ -18,7 +18,7 @@ export const assert = { } }, isValidBuyQuoteInfo(variableName: string, buyQuoteInfo: BuyQuoteInfo): void { - sharedAssert.isBigNumber(`${variableName}.ethPerAssetPrice`, buyQuoteInfo.ethPerAssetPrice); + sharedAssert.isBigNumber(`${variableName}.assetEthAmount`, buyQuoteInfo.assetEthAmount); sharedAssert.isBigNumber(`${variableName}.feeEthAmount`, buyQuoteInfo.feeEthAmount); sharedAssert.isBigNumber(`${variableName}.totalEthAmount`, buyQuoteInfo.totalEthAmount); }, diff --git a/packages/asset-buyer/src/utils/buy_quote_calculator.ts b/packages/asset-buyer/src/utils/buy_quote_calculator.ts index 6a67ed1ed..b15b880c2 100644 --- a/packages/asset-buyer/src/utils/buy_quote_calculator.ts +++ b/packages/asset-buyer/src/utils/buy_quote_calculator.ts @@ -106,28 +106,28 @@ function calculateQuoteInfo( isMakerAssetZrxToken: boolean, ): BuyQuoteInfo { // find the total eth and zrx needed to buy assetAmount from the resultOrders from left to right - let ethAmountToBuyAsset = constants.ZERO_AMOUNT; - let ethAmountToBuyZrx = constants.ZERO_AMOUNT; + let assetEthAmount = constants.ZERO_AMOUNT; + let zrxEthAmount = constants.ZERO_AMOUNT; if (isMakerAssetZrxToken) { - ethAmountToBuyAsset = findEthAmountNeededToBuyZrx(ordersAndFillableAmounts, assetBuyAmount); + assetEthAmount = findEthAmountNeededToBuyZrx(ordersAndFillableAmounts, assetBuyAmount); } else { // find eth and zrx amounts needed to buy const ethAndZrxAmountToBuyAsset = findEthAndZrxAmountNeededToBuyAsset(ordersAndFillableAmounts, assetBuyAmount); - ethAmountToBuyAsset = ethAndZrxAmountToBuyAsset[0]; + assetEthAmount = ethAndZrxAmountToBuyAsset[0]; const zrxAmountToBuyAsset = ethAndZrxAmountToBuyAsset[1]; // find eth amount needed to buy zrx - ethAmountToBuyZrx = findEthAmountNeededToBuyZrx(feeOrdersAndFillableAmounts, zrxAmountToBuyAsset); + zrxEthAmount = findEthAmountNeededToBuyZrx(feeOrdersAndFillableAmounts, zrxAmountToBuyAsset); } - /// find the eth amount needed to buy the affiliate fee - const ethAmountToBuyAffiliateFee = ethAmountToBuyAsset.mul(feePercentage).ceil(); - const totalEthAmountWithoutAffiliateFee = ethAmountToBuyAsset.plus(ethAmountToBuyZrx); - const ethAmountTotal = totalEthAmountWithoutAffiliateFee.plus(ethAmountToBuyAffiliateFee); - // divide into the assetBuyAmount in order to find rate of makerAsset / WETH - const ethPerAssetPrice = totalEthAmountWithoutAffiliateFee.div(assetBuyAmount); + // eth amount needed to buy the affiliate fee + const affiliateFeeEthAmount = assetEthAmount.mul(feePercentage).ceil(); + // eth amount needed for fees is the sum of affiliate fee and zrx fee + const feeEthAmount = affiliateFeeEthAmount.plus(zrxEthAmount); + // eth amount needed in total is the sum of the amount needed for the asset and the amount needed for fees + const totalEthAmount = assetEthAmount.plus(feeEthAmount); return { - totalEthAmount: ethAmountTotal, - feeEthAmount: ethAmountToBuyAffiliateFee, - ethPerAssetPrice, + assetEthAmount, + feeEthAmount, + totalEthAmount, }; } diff --git a/packages/asset-buyer/src/utils/order_provider_response_processor.ts b/packages/asset-buyer/src/utils/order_provider_response_processor.ts index 28f684f3c..25e85b2cc 100644 --- a/packages/asset-buyer/src/utils/order_provider_response_processor.ts +++ b/packages/asset-buyer/src/utils/order_provider_response_processor.ts @@ -44,19 +44,24 @@ export const orderProviderResponseProcessor = { let unsortedOrders = filteredOrders; // if an orderValidator is provided, use on chain information to calculate remaining fillable makerAsset amounts if (!_.isUndefined(orderValidator)) { - // TODO(bmillman): improvement - // try/catch this request and throw a more domain specific error const takerAddresses = _.map(filteredOrders, () => constants.NULL_ADDRESS); - const ordersAndTradersInfo = await orderValidator.getOrdersAndTradersInfoAsync( - filteredOrders, - takerAddresses, - ); - // take orders + on chain information and find the valid orders and remaining fillable maker asset amounts - unsortedOrders = getValidOrdersWithRemainingFillableMakerAssetAmountsFromOnChain( - filteredOrders, - ordersAndTradersInfo, - isMakerAssetZrxToken, - ); + try { + const ordersAndTradersInfo = await orderValidator.getOrdersAndTradersInfoAsync( + filteredOrders, + takerAddresses, + ); + // take orders + on chain information and find the valid orders and remaining fillable maker asset amounts + unsortedOrders = getValidOrdersWithRemainingFillableMakerAssetAmountsFromOnChain( + filteredOrders, + ordersAndTradersInfo, + isMakerAssetZrxToken, + ); + } catch (err) { + // Sometimes we observe this call to orderValidator fail with response `0x` + // Because of differences in Parity / Geth implementations, its very hard to tell if this response is a "system error" + // or a revert. In this case we just swallow these errors and fallback to partial fill information from the SRA. + // TODO(bmillman): report these errors so we have an idea of how often we're getting these failures. + } } // sort orders by rate // TODO(bmillman): optimization diff --git a/packages/asset-buyer/test/buy_quote_calculator_test.ts b/packages/asset-buyer/test/buy_quote_calculator_test.ts index 0ea371982..a30017b72 100644 --- a/packages/asset-buyer/test/buy_quote_calculator_test.ts +++ b/packages/asset-buyer/test/buy_quote_calculator_test.ts @@ -108,17 +108,17 @@ describe('buyQuoteCalculator', () => { // 50 eth to fill the first order + 100 eth for fees const expectedEthAmountForAsset = new BigNumber(50); const expectedEthAmountForZrxFees = new BigNumber(100); - const expectedFillEthAmount = expectedEthAmountForAsset.plus(expectedEthAmountForZrxFees); - const expectedFeeEthAmount = expectedEthAmountForAsset.mul(feePercentage); + const expectedFillEthAmount = expectedEthAmountForAsset; + const expectedAffiliateFeeEthAmount = expectedEthAmountForAsset.mul(feePercentage); + const expectedFeeEthAmount = expectedAffiliateFeeEthAmount.plus(expectedEthAmountForZrxFees); const expectedTotalEthAmount = expectedFillEthAmount.plus(expectedFeeEthAmount); - const expectedEthPerAssetPrice = expectedFillEthAmount.div(assetBuyAmount); + expect(buyQuote.bestCaseQuoteInfo.assetEthAmount).to.bignumber.equal(expectedFillEthAmount); expect(buyQuote.bestCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedFeeEthAmount); expect(buyQuote.bestCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedTotalEthAmount); - expect(buyQuote.bestCaseQuoteInfo.ethPerAssetPrice).to.bignumber.equal(expectedEthPerAssetPrice); // because we have no slippage protection, minRate is equal to maxRate + expect(buyQuote.worstCaseQuoteInfo.assetEthAmount).to.bignumber.equal(expectedFillEthAmount); expect(buyQuote.worstCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedFeeEthAmount); expect(buyQuote.worstCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedTotalEthAmount); - expect(buyQuote.worstCaseQuoteInfo.ethPerAssetPrice).to.bignumber.equal(expectedEthPerAssetPrice); // test if feePercentage gets passed through expect(buyQuote.feePercentage).to.equal(feePercentage); }); @@ -146,23 +146,23 @@ describe('buyQuoteCalculator', () => { // 50 eth to fill the first order + 100 eth for fees const expectedEthAmountForAsset = new BigNumber(50); const expectedEthAmountForZrxFees = new BigNumber(100); - const expectedFillEthAmount = expectedEthAmountForAsset.plus(expectedEthAmountForZrxFees); - const expectedFeeEthAmount = expectedEthAmountForAsset.mul(feePercentage); + const expectedFillEthAmount = expectedEthAmountForAsset; + const expectedAffiliateFeeEthAmount = expectedEthAmountForAsset.mul(feePercentage); + const expectedFeeEthAmount = expectedAffiliateFeeEthAmount.plus(expectedEthAmountForZrxFees); const expectedTotalEthAmount = expectedFillEthAmount.plus(expectedFeeEthAmount); - const expectedEthPerAssetPrice = expectedFillEthAmount.div(assetBuyAmount); + expect(buyQuote.bestCaseQuoteInfo.assetEthAmount).to.bignumber.equal(expectedFillEthAmount); 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 + 208 eth for fees const expectedWorstEthAmountForAsset = new BigNumber(100); const expectedWorstEthAmountForZrxFees = new BigNumber(208); - const expectedWorstFillEthAmount = expectedWorstEthAmountForAsset.plus(expectedWorstEthAmountForZrxFees); - const expectedWorstFeeEthAmount = expectedWorstEthAmountForAsset.mul(feePercentage); + const expectedWorstFillEthAmount = expectedWorstEthAmountForAsset; + const expectedWorstAffiliateFeeEthAmount = expectedWorstEthAmountForAsset.mul(feePercentage); + const expectedWorstFeeEthAmount = expectedWorstAffiliateFeeEthAmount.plus(expectedWorstEthAmountForZrxFees); const expectedWorstTotalEthAmount = expectedWorstFillEthAmount.plus(expectedWorstFeeEthAmount); - const expectedWorstEthPerAssetPrice = expectedWorstFillEthAmount.div(assetBuyAmount); + expect(buyQuote.worstCaseQuoteInfo.assetEthAmount).to.bignumber.equal(expectedWorstFillEthAmount); expect(buyQuote.worstCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedWorstFeeEthAmount); expect(buyQuote.worstCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedWorstTotalEthAmount); - expect(buyQuote.worstCaseQuoteInfo.ethPerAssetPrice).to.bignumber.equal(expectedWorstEthPerAssetPrice); // test if feePercentage gets passed through expect(buyQuote.feePercentage).to.equal(feePercentage); }); diff --git a/packages/base-contract/CHANGELOG.json b/packages/base-contract/CHANGELOG.json index b6ce29874..dc30ac076 100644 --- a/packages/base-contract/CHANGELOG.json +++ b/packages/base-contract/CHANGELOG.json @@ -1,5 +1,23 @@ [ { + "timestamp": 1542208198, + "version": "3.0.6", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { + "timestamp": 1542134075, + "version": "3.0.5", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "timestamp": 1542028948, "version": "3.0.4", "changes": [ diff --git a/packages/base-contract/CHANGELOG.md b/packages/base-contract/CHANGELOG.md index 984eeb3f1..cf1b80b6d 100644 --- a/packages/base-contract/CHANGELOG.md +++ b/packages/base-contract/CHANGELOG.md @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v3.0.6 - _November 14, 2018_ + + * Dependencies updated + +## v3.0.5 - _November 13, 2018_ + + * Dependencies updated + ## v3.0.4 - _November 12, 2018_ * Dependencies updated diff --git a/packages/base-contract/package.json b/packages/base-contract/package.json index d92566208..b1cdc3cc6 100644 --- a/packages/base-contract/package.json +++ b/packages/base-contract/package.json @@ -1,6 +1,6 @@ { "name": "@0x/base-contract", - "version": "3.0.4", + "version": "3.0.6", "engines": { "node": ">=6.12" }, @@ -41,8 +41,8 @@ }, "dependencies": { "@0x/typescript-typings": "^3.0.4", - "@0x/utils": "^2.0.4", - "@0x/web3-wrapper": "^3.1.2", + "@0x/utils": "^2.0.5", + "@0x/web3-wrapper": "^3.1.4", "ethereum-types": "^1.1.2", "ethers": "~4.0.4", "lodash": "^4.17.5" diff --git a/packages/connect/CHANGELOG.json b/packages/connect/CHANGELOG.json index 583c55cfc..6960e1d97 100644 --- a/packages/connect/CHANGELOG.json +++ b/packages/connect/CHANGELOG.json @@ -1,5 +1,23 @@ [ { + "timestamp": 1542208198, + "version": "3.0.6", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { + "timestamp": 1542134075, + "version": "3.0.5", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "timestamp": 1542028948, "version": "3.0.4", "changes": [ diff --git a/packages/connect/CHANGELOG.md b/packages/connect/CHANGELOG.md index 7a57c1f88..13afad09b 100644 --- a/packages/connect/CHANGELOG.md +++ b/packages/connect/CHANGELOG.md @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v3.0.6 - _November 14, 2018_ + + * Dependencies updated + +## v3.0.5 - _November 13, 2018_ + + * Dependencies updated + ## v3.0.4 - _November 12, 2018_ * Dependencies updated diff --git a/packages/connect/package.json b/packages/connect/package.json index 09d8334a7..8e3f3ac8f 100644 --- a/packages/connect/package.json +++ b/packages/connect/package.json @@ -1,6 +1,6 @@ { "name": "@0x/connect", - "version": "3.0.4", + "version": "3.0.6", "engines": { "node": ">=6.12" }, @@ -44,12 +44,12 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/connect/README.md", "dependencies": { - "@0x/assert": "^1.0.16", - "@0x/json-schemas": "^2.1.0", - "@0x/order-utils": "^3.0.0", + "@0x/assert": "^1.0.17", + "@0x/json-schemas": "^2.1.1", + "@0x/order-utils": "^3.0.2", "@0x/types": "^1.2.1", "@0x/typescript-typings": "^3.0.4", - "@0x/utils": "^2.0.4", + "@0x/utils": "^2.0.5", "lodash": "^4.17.5", "query-string": "^5.0.1", "sinon": "^4.0.0", diff --git a/packages/contract-wrappers/CHANGELOG.json b/packages/contract-wrappers/CHANGELOG.json index 425c33643..fbf3972a9 100644 --- a/packages/contract-wrappers/CHANGELOG.json +++ b/packages/contract-wrappers/CHANGELOG.json @@ -1,5 +1,23 @@ [ { + "timestamp": 1542208198, + "version": "4.0.2", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { + "timestamp": 1542134075, + "version": "4.0.1", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "version": "4.0.0", "changes": [ { diff --git a/packages/contract-wrappers/CHANGELOG.md b/packages/contract-wrappers/CHANGELOG.md index 52c3fece6..eca1b4633 100644 --- a/packages/contract-wrappers/CHANGELOG.md +++ b/packages/contract-wrappers/CHANGELOG.md @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v4.0.2 - _November 14, 2018_ + + * Dependencies updated + +## v4.0.1 - _November 13, 2018_ + + * Dependencies updated + ## v4.0.0 - _November 12, 2018_ * Add signature validation, regular cancellation and `cancelledUpTo` checks to `validateOrderFillableOrThrowAsync` (#1235) diff --git a/packages/contract-wrappers/package.json b/packages/contract-wrappers/package.json index ab316c586..2d6b4e3f8 100644 --- a/packages/contract-wrappers/package.json +++ b/packages/contract-wrappers/package.json @@ -1,6 +1,6 @@ { "name": "@0x/contract-wrappers", - "version": "4.0.0", + "version": "4.0.2", "description": "Smart TS wrappers for 0x smart contracts", "keywords": [ "0xproject", @@ -37,9 +37,9 @@ "node": ">=6.0.0" }, "devDependencies": { - "@0x/dev-utils": "^1.0.15", - "@0x/migrations": "^2.0.2", - "@0x/subproviders": "^2.1.2", + "@0x/dev-utils": "^1.0.17", + "@0x/migrations": "^2.0.4", + "@0x/subproviders": "^2.1.4", "@0x/tslint-config": "^1.0.10", "@types/lodash": "4.14.104", "@types/mocha": "^2.2.42", @@ -65,17 +65,17 @@ "web3-provider-engine": "14.0.6" }, "dependencies": { - "@0x/abi-gen-wrappers": "^1.0.3", - "@0x/assert": "^1.0.16", + "@0x/abi-gen-wrappers": "^1.0.5", + "@0x/assert": "^1.0.17", "@0x/contract-addresses": "^1.1.0", "@0x/contract-artifacts": "^1.1.0", - "@0x/fill-scenarios": "^1.0.10", - "@0x/json-schemas": "^2.1.0", - "@0x/order-utils": "^3.0.0", + "@0x/fill-scenarios": "^1.0.12", + "@0x/json-schemas": "^2.1.1", + "@0x/order-utils": "^3.0.2", "@0x/types": "^1.2.1", "@0x/typescript-typings": "^3.0.4", - "@0x/utils": "^2.0.4", - "@0x/web3-wrapper": "^3.1.2", + "@0x/utils": "^2.0.5", + "@0x/web3-wrapper": "^3.1.4", "ethereum-types": "^1.1.2", "ethereumjs-blockstream": "6.0.0", "ethereumjs-util": "^5.1.1", diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 3e08ea2c4..c87e2e95b 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "contracts", - "version": "2.1.52", + "version": "2.1.54", "engines": { "node": ">=6.12" }, @@ -45,11 +45,11 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/contracts/README.md", "devDependencies": { - "@0x/abi-gen": "^1.0.15", - "@0x/dev-utils": "^1.0.15", - "@0x/sol-compiler": "^1.1.10", - "@0x/sol-cov": "^2.1.10", - "@0x/subproviders": "^2.1.2", + "@0x/abi-gen": "^1.0.16", + "@0x/dev-utils": "^1.0.17", + "@0x/sol-compiler": "^1.1.12", + "@0x/sol-cov": "^2.1.12", + "@0x/subproviders": "^2.1.4", "@0x/tslint-config": "^1.0.10", "@types/bn.js": "^4.11.0", "@types/ethereumjs-abi": "^0.6.0", @@ -71,12 +71,12 @@ "yargs": "^10.0.3" }, "dependencies": { - "@0x/base-contract": "^3.0.4", - "@0x/order-utils": "^3.0.0", + "@0x/base-contract": "^3.0.6", + "@0x/order-utils": "^3.0.2", "@0x/types": "^1.2.1", "@0x/typescript-typings": "^3.0.4", - "@0x/utils": "^2.0.4", - "@0x/web3-wrapper": "^3.1.2", + "@0x/utils": "^2.0.5", + "@0x/web3-wrapper": "^3.1.4", "@types/js-combinatorics": "^0.5.29", "bn.js": "^4.11.8", "ethereum-types": "^1.1.2", diff --git a/packages/dev-tools-pages/package.json b/packages/dev-tools-pages/package.json index ab0fa006b..7352263bc 100644 --- a/packages/dev-tools-pages/package.json +++ b/packages/dev-tools-pages/package.json @@ -1,6 +1,6 @@ { "name": "@0x/dev-tools-pages", - "version": "0.0.4", + "version": "0.0.6", "engines": { "node": ">=6.12" }, @@ -16,7 +16,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@0x/react-shared": "^1.0.19", + "@0x/react-shared": "^1.0.21", "@0xproject/react-shared": "^1.0.15", "basscss": "^8.0.3", "bowser": "^1.9.3", diff --git a/packages/dev-utils/CHANGELOG.json b/packages/dev-utils/CHANGELOG.json index f526f6b3e..d6cdd1c5a 100644 --- a/packages/dev-utils/CHANGELOG.json +++ b/packages/dev-utils/CHANGELOG.json @@ -1,5 +1,23 @@ [ { + "timestamp": 1542208198, + "version": "1.0.17", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { + "timestamp": 1542134075, + "version": "1.0.16", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "timestamp": 1542028948, "version": "1.0.15", "changes": [ diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index 4e364f96a..4becadf81 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.17 - _November 14, 2018_ + + * Dependencies updated + +## v1.0.16 - _November 13, 2018_ + + * Dependencies updated + ## v1.0.15 - _November 12, 2018_ * Dependencies updated diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index a4d2381bd..3db391131 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@0x/dev-utils", - "version": "1.0.15", + "version": "1.0.17", "engines": { "node": ">=6.12" }, @@ -41,11 +41,11 @@ "typescript": "3.0.1" }, "dependencies": { - "@0x/subproviders": "^2.1.2", + "@0x/subproviders": "^2.1.4", "@0x/types": "^1.2.1", "@0x/typescript-typings": "^3.0.4", - "@0x/utils": "^2.0.4", - "@0x/web3-wrapper": "^3.1.2", + "@0x/utils": "^2.0.5", + "@0x/web3-wrapper": "^3.1.4", "@types/web3-provider-engine": "^14.0.0", "chai": "^4.0.1", "ethereum-types": "^1.1.2", diff --git a/packages/fill-scenarios/CHANGELOG.json b/packages/fill-scenarios/CHANGELOG.json index bd6e58334..deeae5f5d 100644 --- a/packages/fill-scenarios/CHANGELOG.json +++ b/packages/fill-scenarios/CHANGELOG.json @@ -1,5 +1,23 @@ [ { + "timestamp": 1542208198, + "version": "1.0.12", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { + "timestamp": 1542134075, + "version": "1.0.11", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "timestamp": 1542028948, "version": "1.0.10", "changes": [ diff --git a/packages/fill-scenarios/CHANGELOG.md b/packages/fill-scenarios/CHANGELOG.md index 57a6431b6..c8bd402de 100644 --- a/packages/fill-scenarios/CHANGELOG.md +++ b/packages/fill-scenarios/CHANGELOG.md @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.12 - _November 14, 2018_ + + * Dependencies updated + +## v1.0.11 - _November 13, 2018_ + + * Dependencies updated + ## v1.0.10 - _November 12, 2018_ * Dependencies updated diff --git a/packages/fill-scenarios/package.json b/packages/fill-scenarios/package.json index 20825b6dd..2d9bff0e7 100644 --- a/packages/fill-scenarios/package.json +++ b/packages/fill-scenarios/package.json @@ -1,6 +1,6 @@ { "name": "@0x/fill-scenarios", - "version": "1.0.10", + "version": "1.0.12", "description": "0x order fill scenario generator", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -28,14 +28,14 @@ "typescript": "3.0.1" }, "dependencies": { - "@0x/abi-gen-wrappers": "^1.0.3", - "@0x/base-contract": "^3.0.4", + "@0x/abi-gen-wrappers": "^1.0.5", + "@0x/base-contract": "^3.0.6", "@0x/contract-artifacts": "^1.1.0", - "@0x/order-utils": "^3.0.0", + "@0x/order-utils": "^3.0.2", "@0x/types": "^1.2.1", "@0x/typescript-typings": "^3.0.4", - "@0x/utils": "^2.0.4", - "@0x/web3-wrapper": "^3.1.2", + "@0x/utils": "^2.0.5", + "@0x/web3-wrapper": "^3.1.4", "ethereum-types": "^1.1.2", "ethers": "~4.0.4", "lodash": "^4.17.5" diff --git a/packages/instant/package.json b/packages/instant/package.json index 4ec9e9bd3..6caa3902b 100644 --- a/packages/instant/package.json +++ b/packages/instant/package.json @@ -1,6 +1,6 @@ { "name": "@0x/instant", - "version": "0.0.5", + "version": "0.0.7", "engines": { "node": ">=6.12" }, @@ -45,15 +45,16 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/instant/README.md", "dependencies": { - "@0x/assert": "^1.0.16", - "@0x/asset-buyer": "^2.2.1", - "@0x/json-schemas": "^2.1.0", - "@0x/order-utils": "^3.0.0", - "@0x/subproviders": "^2.1.2", + "@0x/assert": "^1.0.17", + "@0x/asset-buyer": "^3.0.0", + "@0x/json-schemas": "^2.1.1", + "@0x/order-utils": "^3.0.2", + "@0x/subproviders": "^2.1.4", "@0x/types": "^1.2.1", "@0x/typescript-typings": "^3.0.4", - "@0x/utils": "^2.0.4", - "@0x/web3-wrapper": "^3.1.2", + "@0x/utils": "^2.0.5", + "@0x/web3-wrapper": "^3.1.4", + "bowser": "^1.9.4", "copy-to-clipboard": "^3.0.8", "ethereum-types": "^1.1.2", "lodash": "^4.17.10", @@ -82,6 +83,7 @@ "awesome-typescript-loader": "^5.2.1", "enzyme": "^3.6.0", "enzyme-adapter-react-16": "^1.5.0", + "ip": "^1.1.5", "jest": "^23.6.0", "make-promises-safe": "^1.1.0", "npm-run-all": "^4.1.2", diff --git a/packages/instant/src/components/animations/slide_animation.tsx b/packages/instant/src/components/animations/slide_animation.tsx index 9adb1c674..5992bcba7 100644 --- a/packages/instant/src/components/animations/slide_animation.tsx +++ b/packages/instant/src/components/animations/slide_animation.tsx @@ -1,10 +1,10 @@ import * as React from 'react'; import { OptionallyScreenSpecific } from '../../style/media'; +import { SlideAnimationState } from '../../types'; import { PositionAnimation, PositionAnimationSettings } from './position_animation'; -export type SlideAnimationState = 'slidIn' | 'slidOut' | 'none'; export interface SlideAnimationProps { animationState: SlideAnimationState; slideInSettings: OptionallyScreenSpecific<PositionAnimationSettings>; diff --git a/packages/instant/src/components/buy_button.tsx b/packages/instant/src/components/buy_button.tsx index 877ab275c..8b6121e43 100644 --- a/packages/instant/src/components/buy_button.tsx +++ b/packages/instant/src/components/buy_button.tsx @@ -43,7 +43,6 @@ export class BuyButton extends React.Component<BuyButtonProps> { onClick={this._handleClick} isDisabled={shouldDisableButton} fontColor={ColorOption.white} - fontSize="20px" > Buy </Button> diff --git a/packages/instant/src/components/buy_order_progress.tsx b/packages/instant/src/components/buy_order_progress.tsx index bc7319423..6568de91b 100644 --- a/packages/instant/src/components/buy_order_progress.tsx +++ b/packages/instant/src/components/buy_order_progress.tsx @@ -12,7 +12,6 @@ export interface BuyOrderProgressProps { export const BuyOrderProgress: React.StatelessComponent<BuyOrderProgressProps> = props => { const { buyOrderState } = props; - if ( buyOrderState.processState === OrderProcessState.Processing || buyOrderState.processState === OrderProcessState.Success || @@ -30,6 +29,5 @@ export const BuyOrderProgress: React.StatelessComponent<BuyOrderProgressProps> = </Container> ); } - return null; }; diff --git a/packages/instant/src/components/buy_order_state_buttons.tsx b/packages/instant/src/components/buy_order_state_buttons.tsx index 6041bf4f5..e563bec73 100644 --- a/packages/instant/src/components/buy_order_state_buttons.tsx +++ b/packages/instant/src/components/buy_order_state_buttons.tsx @@ -35,7 +35,7 @@ export const BuyOrderStateButtons: React.StatelessComponent<BuyOrderStateButtonP if (props.buyOrderProcessingState === OrderProcessState.Failure) { return ( <Flex justify="space-between"> - <Button width="48%" onClick={props.onRetry} fontColor={ColorOption.white} fontSize="16px"> + <Button width="48%" onClick={props.onRetry} fontColor={ColorOption.white}> Back </Button> <SecondaryButton width="48%" onClick={props.onViewTransaction}> diff --git a/packages/instant/src/components/coinbase_wallet_logo.tsx b/packages/instant/src/components/coinbase_wallet_logo.tsx new file mode 100644 index 000000000..845b96d73 --- /dev/null +++ b/packages/instant/src/components/coinbase_wallet_logo.tsx @@ -0,0 +1,23 @@ +import * as React from 'react'; + +export interface CoinbaseWalletLogoProps { + width?: number; +} + +export const CoinbaseWalletLogo: React.StatelessComponent<CoinbaseWalletLogoProps> = ({ width }) => ( + <svg width={width} viewBox="0 0 51 51" fill="none" xmlns="http://www.w3.org/2000/svg"> + <circle cx="25.5" cy="25.5" r="25.5" fill="#3263E9" /> + <path + fillRule="evenodd" + clipRule="evenodd" + d="M25.5 41C34.0604 41 41 34.0604 41 25.5C41 16.9396 34.0604 10 25.5 10C16.9396 10 10 16.9396 10 25.5C10 34.0604 16.9396 41 25.5 41ZM21.5108 20.5107C20.9586 20.5107 20.5108 20.9584 20.5108 21.5107V29.6223C20.5108 30.1746 20.9586 30.6223 21.5108 30.6223H29.6224C30.1747 30.6223 30.6224 30.1746 30.6224 29.6223V21.5107C30.6224 20.9584 30.1747 20.5107 29.6224 20.5107H21.5108Z" + fill="white" + /> + </svg> +); + +CoinbaseWalletLogo.displayName = 'CoinbaseWalletLogo'; + +CoinbaseWalletLogo.defaultProps = { + width: 164, +}; diff --git a/packages/instant/src/components/erc20_token_selector.tsx b/packages/instant/src/components/erc20_token_selector.tsx index 3503ff31a..e4d8749a9 100644 --- a/packages/instant/src/components/erc20_token_selector.tsx +++ b/packages/instant/src/components/erc20_token_selector.tsx @@ -29,13 +29,19 @@ export class ERC20TokenSelector extends React.Component<ERC20TokenSelectorProps> const { tokens, onTokenSelect } = this.props; return ( <Container height="100%"> + <Container marginBottom="10px"> + <Text fontColor={ColorOption.darkGrey} fontSize="18px" fontWeight="600" lineHeight="22px"> + Select Token + </Text> + </Container> <SearchInput placeholder="Search tokens..." width="100%" value={this.state.searchQuery} onChange={this._handleSearchInputChange} + tabIndex={-1} /> - <Container overflow="scroll" height="calc(100% - 80px)" marginTop="10px"> + <Container overflow="scroll" height="calc(100% - 90px)" marginTop="10px"> {_.map(tokens, token => { if (!this._isTokenQueryMatch(token)) { return null; @@ -57,8 +63,10 @@ export class ERC20TokenSelector extends React.Component<ERC20TokenSelectorProps> if (_.isUndefined(searchQuery)) { return true; } - const stringToSearch = `${token.metaData.name} ${token.metaData.symbol}`; - return _.includes(stringToSearch.toLowerCase(), searchQuery.toLowerCase()); + const searchQueryLowerCase = searchQuery.toLowerCase(); + const tokenName = token.metaData.name.toLowerCase(); + const tokenSymbol = token.metaData.symbol.toLowerCase(); + return _.startsWith(tokenSymbol, searchQueryLowerCase) || _.startsWith(tokenName, searchQueryLowerCase); }; } diff --git a/packages/instant/src/components/install_wallet_panel_content.tsx b/packages/instant/src/components/install_wallet_panel_content.tsx new file mode 100644 index 000000000..88c26f59c --- /dev/null +++ b/packages/instant/src/components/install_wallet_panel_content.tsx @@ -0,0 +1,68 @@ +import * as React from 'react'; + +import { + META_MASK_CHROME_STORE_URL, + META_MASK_FIREFOX_STORE_URL, + META_MASK_OPERA_STORE_URL, + META_MASK_SITE_URL, +} from '../constants'; +import { ColorOption } from '../style/theme'; +import { Browser } from '../types'; +import { envUtil } from '../util/env'; + +import { MetaMaskLogo } from './meta_mask_logo'; +import { StandardPanelContent, StandardPanelContentProps } from './standard_panel_content'; +import { Button } from './ui/button'; + +export interface InstallWalletPanelContentProps {} + +export class InstallWalletPanelContent extends React.Component<InstallWalletPanelContentProps> { + public render(): React.ReactNode { + const panelProps = this._getStandardPanelContentProps(); + return <StandardPanelContent {...panelProps} />; + } + private readonly _getStandardPanelContentProps = (): StandardPanelContentProps => { + const browser = envUtil.getBrowser(); + let description = 'Please install the MetaMask wallet browser extension.'; + let actionText = 'Learn More'; + let actionUrl = META_MASK_SITE_URL; + switch (browser) { + case Browser.Chrome: + description = 'Please install the MetaMask wallet browser extension from the Chrome Store.'; + actionText = 'Get Chrome Extension'; + actionUrl = META_MASK_CHROME_STORE_URL; + break; + case Browser.Firefox: + description = 'Please install the MetaMask wallet browser extension from the Firefox Store.'; + actionText = 'Get Firefox Extension'; + actionUrl = META_MASK_FIREFOX_STORE_URL; + break; + case Browser.Opera: + description = 'Please install the MetaMask wallet browser extension from the Opera Store.'; + actionText = 'Get Opera Add-on'; + actionUrl = META_MASK_OPERA_STORE_URL; + break; + default: + break; + } + return { + image: <MetaMaskLogo width={85} height={80} />, + title: 'Install MetaMask', + description, + moreInfoSettings: { + href: META_MASK_SITE_URL, + text: 'What is MetaMask?', + }, + action: ( + <Button + href={actionUrl} + width="100%" + fontColor={ColorOption.white} + backgroundColor={ColorOption.darkOrange} + > + {actionText} + </Button> + ), + }; + }; +} diff --git a/packages/instant/src/components/instant_heading.tsx b/packages/instant/src/components/instant_heading.tsx index b07776b2c..002695269 100644 --- a/packages/instant/src/components/instant_heading.tsx +++ b/packages/instant/src/components/instant_heading.tsx @@ -15,8 +15,8 @@ import { Spinner } from './ui/spinner'; import { Text } from './ui/text'; export interface InstantHeadingProps { - selectedAssetAmount?: BigNumber; - totalEthBaseAmount?: BigNumber; + selectedAssetUnitAmount?: BigNumber; + totalEthBaseUnitAmount?: BigNumber; ethUsdPrice?: BigNumber; quoteRequestState: AsyncProcessState; buyOrderState: OrderState; @@ -32,12 +32,7 @@ export class InstantHeading extends React.Component<InstantHeadingProps, {}> { public render(): React.ReactNode { const iconOrAmounts = this._renderIcon() || this._renderAmountsSection(); return ( - <Container - backgroundColor={ColorOption.primaryColor} - padding="20px" - width="100%" - borderRadius="3px 3px 0px 0px" - > + <Container backgroundColor={ColorOption.primaryColor} padding="20px" width="100%"> <Container marginBottom="5px"> <Text letterSpacing="1px" @@ -104,7 +99,7 @@ export class InstantHeading extends React.Component<InstantHeadingProps, {}> { if (this.props.quoteRequestState === AsyncProcessState.Pending) { return <AmountPlaceholder isPulsating={true} color={PLACEHOLDER_COLOR} />; } - if (_.isUndefined(this.props.selectedAssetAmount)) { + if (_.isUndefined(this.props.selectedAssetUnitAmount)) { return <AmountPlaceholder isPulsating={false} color={PLACEHOLDER_COLOR} />; } return amountFunction(); @@ -113,8 +108,8 @@ export class InstantHeading extends React.Component<InstantHeadingProps, {}> { private readonly _renderEthAmount = (): React.ReactNode => { return ( <Text fontSize="16px" fontColor={ColorOption.white} fontWeight={500}> - {format.ethBaseAmount( - this.props.totalEthBaseAmount, + {format.ethBaseUnitAmount( + this.props.totalEthBaseUnitAmount, 4, <AmountPlaceholder isPulsating={false} color={PLACEHOLDER_COLOR} />, )} @@ -125,8 +120,8 @@ export class InstantHeading extends React.Component<InstantHeadingProps, {}> { private readonly _renderDollarAmount = (): React.ReactNode => { return ( <Text fontSize="16px" fontColor={ColorOption.white}> - {format.ethBaseAmountInUsd( - this.props.totalEthBaseAmount, + {format.ethBaseUnitAmountInUsd( + this.props.totalEthBaseUnitAmount, this.props.ethUsdPrice, 2, <AmountPlaceholder isPulsating={false} color={ColorOption.white} />, diff --git a/packages/instant/src/components/meta_mask_logo.tsx b/packages/instant/src/components/meta_mask_logo.tsx new file mode 100644 index 000000000..bfbc67270 --- /dev/null +++ b/packages/instant/src/components/meta_mask_logo.tsx @@ -0,0 +1,80 @@ +import * as React from 'react'; + +export interface MetaMaskLogoProps { + width?: number; + height?: number; +} + +export const MetaMaskLogo: React.StatelessComponent<MetaMaskLogoProps> = ({ width, height }) => ( + <svg width={width} height={height} viewBox="0 0 85 80" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path d="M80.578 0L47.7107 24.8648L53.542 10.2702L80.578 0Z" fill="#E2761B" /> + <path d="M4.24075 0L37.1081 25.4053L31.2768 10.2702L4.24075 0Z" fill="#E4761B" /> + <path d="M68.9152 57.8379L59.9032 71.8919L78.9874 77.2973L84.2886 58.3785L68.9152 57.8379Z" fill="#E4761B" /> + <path d="M0.53006 58.3785L5.83124 77.2973L24.9155 71.8919L15.9035 57.8379L0.53006 58.3785Z" fill="#E4761B" /> + <path d="M23.8552 34.5941L18.554 42.7022L37.1082 43.7833L36.5781 23.2428L23.8552 34.5941Z" fill="#E4761B" /> + <path d="M60.9635 34.5941L47.7106 23.2428V43.7833L66.2647 42.7022L60.9635 34.5941Z" fill="#E4761B" /> + <path d="M24.9156 71.8914L36.0481 66.4861L26.5059 58.378L24.9156 71.8914Z" fill="#E4761B" /> + <path d="M48.7709 66.4861L59.9034 71.8914L58.313 58.378L48.7709 66.4861Z" fill="#E4761B" /> + <path d="M59.9034 71.8919L48.7709 66.4865L49.301 73.5135V76.7567L59.9034 71.8919Z" fill="#D7C1B3" /> + <path d="M24.9157 71.892L35.518 76.7568V73.5136L36.0482 66.4866L24.9157 71.892Z" fill="#D7C1B3" /> + <path d="M35.5179 53.5138L25.9758 50.8111L32.8673 47.5678L35.5179 53.5138Z" fill="#233447" /> + <path d="M49.3009 53.5138L51.9515 47.5678L58.843 50.8111L49.3009 53.5138Z" fill="#233447" /> + <path d="M24.9155 71.892L26.5059 57.838L15.9035 58.3785L24.9155 71.892Z" fill="#CD6116" /> + <path d="M58.313 57.838L59.9034 71.892L68.9154 58.3785L58.313 57.838Z" fill="#CD6116" /> + <path + d="M66.2648 42.7025L47.7106 43.7836L49.301 53.5132L51.9516 47.5673L58.8431 50.8106L66.2648 42.7025Z" + fill="#CD6116" + /> + <path + d="M25.9758 50.8106L32.8673 47.5673L35.5179 53.5132L37.1083 43.7836L18.5541 42.7025L25.9758 50.8106Z" + fill="#CD6116" + /> + <path d="M18.5541 42.7024L26.5059 58.378L25.9758 50.8105L18.5541 42.7024Z" fill="#E4751F" /> + <path d="M58.8431 50.8106L58.313 58.3781L66.2647 42.7025L58.8431 50.8106Z" fill="#E4751F" /> + <path d="M37.1083 43.7838L35.518 53.5135L37.6384 65.4053L38.1686 49.7297L37.1083 43.7838Z" fill="#E4751F" /> + <path d="M47.7105 43.7838L46.6503 49.7297L47.1804 65.4053L49.3009 53.5135L47.7105 43.7838Z" fill="#E4751F" /> + <path + d="M49.301 53.5134L47.1805 65.4052L48.7709 66.4863L58.313 58.3782L58.8431 50.8107L49.301 53.5134Z" + fill="#F6851B" + /> + <path + d="M25.9758 50.8107L26.5059 58.3782L36.048 66.4863L37.6384 65.4052L35.5179 53.5134L25.9758 50.8107Z" + fill="#F6851B" + /> + <path + d="M49.3011 76.7568V73.5135L48.771 72.973H36.0482L35.518 73.5135V76.7568L24.9157 71.8919L28.6265 75.1351L36.0482 80H48.771L56.1927 75.1351L59.9035 71.8919L49.3011 76.7568Z" + fill="#C0AD9E" + /> + <path + d="M48.771 66.486L47.1806 65.405H37.6385L36.0482 66.486L35.518 73.513L36.0482 72.9725H48.771L49.3011 73.513L48.771 66.486Z" + fill="#161616" + /> + <path + d="M82.1685 26.4864L84.8191 12.9729L80.5781 0L48.771 24.3242L60.9637 34.5945L78.4576 39.9998L82.1685 35.6755L80.5781 34.0539L83.2287 31.8918L81.1082 30.2702L83.7588 28.108L82.1685 26.4864Z" + fill="#763D16" + /> + <path + d="M0 12.9729L2.65059 26.4864L1.06024 28.108L3.71083 30.2702L1.59036 31.8918L4.24095 34.0539L2.65059 35.6755L6.36142 39.9998L23.8553 34.5945L36.0481 24.3242L4.24095 0L0 12.9729Z" + fill="#763D16" + /> + <path + d="M78.4575 39.9993L60.9636 34.5939L66.2648 42.702L58.313 58.3776H68.9154H84.2888L78.4575 39.9993Z" + fill="#F6851B" + /> + <path + d="M23.8554 34.5939L6.36147 39.9993L0.530167 58.3776H15.9036H26.506L18.5542 42.702L23.8554 34.5939Z" + fill="#F6851B" + /> + <path + d="M47.7106 43.7833L48.7709 24.3239L53.5419 10.2699H31.2769L36.048 24.3239L37.1083 43.7833L37.6384 49.7292V65.4048H47.1805V49.7292L47.7106 43.7833Z" + fill="#F6851B" + /> + </svg> +); + +MetaMaskLogo.displayName = 'MetaMaskLogo'; + +MetaMaskLogo.defaultProps = { + width: 85, + height: 80, +}; diff --git a/packages/instant/src/components/order_details.tsx b/packages/instant/src/components/order_details.tsx index 9abd7137e..5fc956e1c 100644 --- a/packages/instant/src/components/order_details.tsx +++ b/packages/instant/src/components/order_details.tsx @@ -4,6 +4,7 @@ import * as _ from 'lodash'; import * as React from 'react'; import { oc } from 'ts-optchain'; +import { BIG_NUMBER_ZERO } from '../constants'; import { ColorOption } from '../style/theme'; import { format } from '../util/format'; @@ -15,16 +16,23 @@ import { Text } from './ui/text'; export interface OrderDetailsProps { buyQuoteInfo?: BuyQuoteInfo; + selectedAssetUnitAmount?: BigNumber; ethUsdPrice?: BigNumber; isLoading: boolean; } export class OrderDetails extends React.Component<OrderDetailsProps> { public render(): React.ReactNode { - const { buyQuoteInfo, ethUsdPrice } = this.props; + const { buyQuoteInfo, ethUsdPrice, selectedAssetUnitAmount } = this.props; const buyQuoteAccessor = oc(buyQuoteInfo); - const ethAssetPrice = buyQuoteAccessor.ethPerAssetPrice(); - const ethTokenFee = buyQuoteAccessor.feeEthAmount(); - const totalEthAmount = buyQuoteAccessor.totalEthAmount(); + const assetEthBaseUnitAmount = buyQuoteAccessor.assetEthAmount(); + const feeEthBaseUnitAmount = buyQuoteAccessor.feeEthAmount(); + const totalEthBaseUnitAmount = buyQuoteAccessor.totalEthAmount(); + const pricePerTokenEth = + !_.isUndefined(assetEthBaseUnitAmount) && + !_.isUndefined(selectedAssetUnitAmount) && + !selectedAssetUnitAmount.eq(BIG_NUMBER_ZERO) + ? assetEthBaseUnitAmount.div(selectedAssetUnitAmount).ceil() + : undefined; return ( <Container padding="20px" width="100%" flexGrow={1}> <Container marginBottom="10px"> @@ -40,20 +48,19 @@ export class OrderDetails extends React.Component<OrderDetailsProps> { </Container> <EthAmountRow rowLabel="Token Price" - ethAmount={ethAssetPrice} + ethAmount={pricePerTokenEth} ethUsdPrice={ethUsdPrice} - isEthAmountInBaseUnits={false} isLoading={this.props.isLoading} /> <EthAmountRow rowLabel="Fee" - ethAmount={ethTokenFee} + ethAmount={feeEthBaseUnitAmount} ethUsdPrice={ethUsdPrice} isLoading={this.props.isLoading} /> <EthAmountRow rowLabel="Total Cost" - ethAmount={totalEthAmount} + ethAmount={totalEthBaseUnitAmount} ethUsdPrice={ethUsdPrice} shouldEmphasize={true} isLoading={this.props.isLoading} @@ -81,7 +88,7 @@ export class EthAmountRow extends React.Component<EthAmountRowProps> { const { rowLabel, ethAmount, isEthAmountInBaseUnits, shouldEmphasize, isLoading } = this.props; const fontWeight = shouldEmphasize ? 700 : 400; - const ethFormatter = isEthAmountInBaseUnits ? format.ethBaseAmount : format.ethUnitAmount; + const ethFormatter = isEthAmountInBaseUnits ? format.ethBaseUnitAmount : format.ethUnitAmount; return ( <Container padding="10px 0px" borderTop="1px dashed" borderColor={ColorOption.feintGrey}> <Flex justify="space-between"> @@ -105,7 +112,9 @@ export class EthAmountRow extends React.Component<EthAmountRowProps> { ); } private _renderUsdSection(): React.ReactNode { - const usdFormatter = this.props.isEthAmountInBaseUnits ? format.ethBaseAmountInUsd : format.ethUnitAmountInUsd; + const usdFormatter = this.props.isEthAmountInBaseUnits + ? format.ethBaseUnitAmountInUsd + : format.ethUnitAmountInUsd; const shouldHideUsdPriceSection = _.isUndefined(this.props.ethUsdPrice) || _.isUndefined(this.props.ethAmount); return shouldHideUsdPriceSection ? null : ( <Container marginRight="3px" display="inline-block"> diff --git a/packages/instant/src/components/payment_method.tsx b/packages/instant/src/components/payment_method.tsx index 8c0b47d72..ebcd62f35 100644 --- a/packages/instant/src/components/payment_method.tsx +++ b/packages/instant/src/components/payment_method.tsx @@ -1,45 +1,114 @@ -import { BigNumber } from '@0x/utils'; import * as _ from 'lodash'; import * as React from 'react'; import { ColorOption } from '../style/theme'; -import { Network } from '../types'; +import { Account, AccountState, Network } from '../types'; +import { envUtil } from '../util/env'; +import { CoinbaseWalletLogo } from './coinbase_wallet_logo'; +import { MetaMaskLogo } from './meta_mask_logo'; import { PaymentMethodDropdown } from './payment_method_dropdown'; import { Circle } from './ui/circle'; import { Container } from './ui/container'; import { Flex } from './ui/flex'; +import { Icon } from './ui/icon'; import { Text } from './ui/text'; +import { WalletPrompt } from './wallet_prompt'; -export interface PaymentMethodProps {} +export interface PaymentMethodProps { + account: Account; + network: Network; + walletName: string; + onInstallWalletClick: () => void; + onUnlockWalletClick: () => void; +} -export const PaymentMethod: React.StatelessComponent<PaymentMethodProps> = () => ( - <Container padding="20px" width="100%"> - <Container marginBottom="10px"> - <Flex justify="space-between"> - <Text - letterSpacing="1px" - fontColor={ColorOption.primaryColor} - fontWeight={600} - textTransform="uppercase" - fontSize="14px" - > - Payment Method - </Text> +export class PaymentMethod extends React.Component<PaymentMethodProps> { + public render(): React.ReactNode { + return ( + <Container padding="20px" width="100%"> + <Container marginBottom="12px"> + <Flex justify="space-between"> + <Text + letterSpacing="1px" + fontColor={ColorOption.primaryColor} + fontWeight={600} + textTransform="uppercase" + fontSize="14px" + > + {this._renderTitleText()} + </Text> + {this._renderTitleLabel()} + </Flex> + </Container> + {this._renderMainContent()} + </Container> + ); + } + private readonly _renderTitleText = (): string => { + const { account } = this.props; + switch (account.state) { + case AccountState.Loading: + return 'loading...'; + case AccountState.Locked: + case AccountState.None: + return 'connect your wallet'; + case AccountState.Ready: + return 'payment method'; + } + }; + private readonly _renderTitleLabel = (): React.ReactNode => { + const { account } = this.props; + if (account.state === AccountState.Ready || account.state === AccountState.Locked) { + const circleColor: ColorOption = account.state === AccountState.Ready ? ColorOption.green : ColorOption.red; + return ( <Flex> - <Circle color={ColorOption.green} diameter={8} /> + <Circle diameter={8} color={circleColor} /> <Container marginLeft="3px"> <Text fontColor={ColorOption.darkGrey} fontSize="12px"> - MetaMask + {this.props.walletName} </Text> </Container> </Flex> - </Flex> - </Container> - <PaymentMethodDropdown - accountAddress="0xa1b2c3d4e5f6g7h8j9k10" - accountEthBalanceInWei={new BigNumber(10500000000000000000)} - network={Network.Mainnet} - /> - </Container> -); + ); + } + return null; + }; + private readonly _renderMainContent = (): React.ReactNode => { + const { account, network } = this.props; + const isMobile = envUtil.isMobileOperatingSystem(); + const logo = isMobile ? <CoinbaseWalletLogo width={22} /> : <MetaMaskLogo width={19} height={18} />; + const primaryColor = isMobile ? ColorOption.darkBlue : ColorOption.darkOrange; + const secondaryColor = isMobile ? ColorOption.lightBlue : ColorOption.lightOrange; + const colors = { primaryColor, secondaryColor }; + switch (account.state) { + case AccountState.Loading: + // Just take up the same amount of space as the other states. + return <Container height="52px" />; + case AccountState.Locked: + return ( + <WalletPrompt + onClick={this.props.onUnlockWalletClick} + image={<Icon width={13} icon="lock" color={ColorOption.black} />} + {...colors} + > + Please Unlock {this.props.walletName} + </WalletPrompt> + ); + case AccountState.None: + return ( + <WalletPrompt onClick={this.props.onInstallWalletClick} image={logo} {...colors}> + {isMobile ? 'Install Coinbase Wallet' : 'Install MetaMask'} + </WalletPrompt> + ); + case AccountState.Ready: + return ( + <PaymentMethodDropdown + accountAddress={account.address} + accountEthBalanceInWei={account.ethBalanceInWei} + network={network} + /> + ); + } + }; +} diff --git a/packages/instant/src/components/payment_method_dropdown.tsx b/packages/instant/src/components/payment_method_dropdown.tsx index bdce2a49d..b330dbcd6 100644 --- a/packages/instant/src/components/payment_method_dropdown.tsx +++ b/packages/instant/src/components/payment_method_dropdown.tsx @@ -3,6 +3,7 @@ import copy from 'copy-to-clipboard'; import * as React from 'react'; import { Network } from '../types'; +import { envUtil } from '../util/env'; import { etherscanUtil } from '../util/etherscan'; import { format } from '../util/format'; @@ -18,10 +19,13 @@ export class PaymentMethodDropdown extends React.Component<PaymentMethodDropdown public render(): React.ReactNode { const { accountAddress, accountEthBalanceInWei } = this.props; const value = format.ethAddress(accountAddress); - const label = format.ethBaseAmount(accountEthBalanceInWei, 4, '') as string; + const label = format.ethBaseUnitAmount(accountEthBalanceInWei, 4, '') as string; return <Dropdown value={value} label={label} items={this._getDropdownItemConfigs()} />; } private readonly _getDropdownItemConfigs = (): DropdownItemConfig[] => { + if (envUtil.isMobileOperatingSystem()) { + return []; + } const viewOnEtherscan = { text: 'View on Etherscan', onClick: this._handleEtherscanClick, diff --git a/packages/instant/src/components/placing_order_button.tsx b/packages/instant/src/components/placing_order_button.tsx index d774d7d27..2516b90b1 100644 --- a/packages/instant/src/components/placing_order_button.tsx +++ b/packages/instant/src/components/placing_order_button.tsx @@ -7,9 +7,9 @@ import { Container } from './ui/container'; import { Spinner } from './ui/spinner'; export const PlacingOrderButton: React.StatelessComponent<{}> = props => ( - <Button isDisabled={true} width="100%" fontColor={ColorOption.white} fontSize="20px"> + <Button isDisabled={true} width="100%" fontColor={ColorOption.white}> <Container display="inline-block" position="relative" top="3px" marginRight="8px"> - <Spinner widthPx={20} heightPx={20} /> + <Spinner widthPx={16} heightPx={16} /> </Container> Placing Order… </Button> diff --git a/packages/instant/src/components/scaling_input.tsx b/packages/instant/src/components/scaling_input.tsx index 1abadb78b..e1599a316 100644 --- a/packages/instant/src/components/scaling_input.tsx +++ b/packages/instant/src/components/scaling_input.tsx @@ -156,8 +156,6 @@ export class ScalingInput extends React.Component<ScalingInputProps, ScalingInpu return `${width}px`; } return `${textLengthThreshold}ch`; - default: - return '1ch'; } }; private readonly _calculateFontSize = (phase: ScalingInputPhase): number => { diff --git a/packages/instant/src/components/secondary_button.tsx b/packages/instant/src/components/secondary_button.tsx index df0539606..705390e28 100644 --- a/packages/instant/src/components/secondary_button.tsx +++ b/packages/instant/src/components/secondary_button.tsx @@ -15,8 +15,6 @@ export const SecondaryButton: React.StatelessComponent<SecondaryButtonProps> = p borderColor={ColorOption.lightGrey} width={props.width} onClick={props.onClick} - fontColor={ColorOption.primaryColor} - fontSize="16px" {...buttonProps} > {props.children} diff --git a/packages/instant/src/components/sliding_error.tsx b/packages/instant/src/components/sliding_error.tsx index a8d4e391c..b59e2a905 100644 --- a/packages/instant/src/components/sliding_error.tsx +++ b/packages/instant/src/components/sliding_error.tsx @@ -3,9 +3,10 @@ import * as React from 'react'; import { ScreenSpecification } from '../style/media'; import { ColorOption } from '../style/theme'; import { zIndex } from '../style/z_index'; +import { SlideAnimationState } from '../types'; import { PositionAnimationSettings } from './animations/position_animation'; -import { SlideAnimation, SlideAnimationState } from './animations/slide_animation'; +import { SlideAnimation } from './animations/slide_animation'; import { Container } from './ui/container'; import { Flex } from './ui/flex'; diff --git a/packages/instant/src/components/sliding_panel.tsx b/packages/instant/src/components/sliding_panel.tsx index 9d16f9560..7f9037049 100644 --- a/packages/instant/src/components/sliding_panel.tsx +++ b/packages/instant/src/components/sliding_panel.tsx @@ -2,35 +2,25 @@ import * as React from 'react'; import { ColorOption } from '../style/theme'; import { zIndex } from '../style/z_index'; +import { SlideAnimationState } from '../types'; import { PositionAnimationSettings } from './animations/position_animation'; -import { SlideAnimation, SlideAnimationState } from './animations/slide_animation'; +import { SlideAnimation } from './animations/slide_animation'; import { Container } from './ui/container'; import { Flex } from './ui/flex'; import { Icon } from './ui/icon'; -import { Text } from './ui/text'; export interface PanelProps { - title?: string; onClose?: () => void; } -export const Panel: React.StatelessComponent<PanelProps> = ({ title, children, onClose }) => ( +export const Panel: React.StatelessComponent<PanelProps> = ({ children, onClose }) => ( <Container backgroundColor={ColorOption.white} width="100%" height="100%" zIndex={zIndex.panel} padding="20px"> - <Flex justify="space-between"> - {title && ( - <Container marginTop="3px"> - <Text fontColor={ColorOption.darkGrey} fontSize="18px" fontWeight="600" lineHeight="22px"> - {title} - </Text> - </Container> - )} - <Container position="relative" bottom="7px"> - <Icon width={12} color={ColorOption.lightGrey} icon="closeX" onClick={onClose} /> - </Container> + <Flex justify="flex-end"> + <Icon padding="5px" width={12} color={ColorOption.lightGrey} icon="closeX" onClick={onClose} /> </Flex> - <Container marginTop="10px" height="100%"> + <Container position="relative" top="-10px" height="100%"> {children} </Container> </Container> diff --git a/packages/instant/src/components/standard_panel_content.tsx b/packages/instant/src/components/standard_panel_content.tsx new file mode 100644 index 000000000..582b3318e --- /dev/null +++ b/packages/instant/src/components/standard_panel_content.tsx @@ -0,0 +1,62 @@ +import * as React from 'react'; + +import { ColorOption } from '../style/theme'; + +import { Container } from './ui/container'; +import { Flex } from './ui/flex'; +import { Text } from './ui/text'; + +export interface MoreInfoSettings { + text: string; + href: string; +} + +export interface StandardPanelContentProps { + image: React.ReactNode; + title?: string; + description: string; + moreInfoSettings?: MoreInfoSettings; + action: React.ReactNode; +} + +const SPACING_BETWEEN_PX = '20px'; + +export const StandardPanelContent: React.StatelessComponent<StandardPanelContentProps> = ({ + image, + title, + description, + moreInfoSettings, + action, +}) => ( + <Container height="100%"> + <Flex direction="column" height="calc(100% - 58px)"> + <Container marginBottom={SPACING_BETWEEN_PX}>{image}</Container> + {title && ( + <Container marginBottom={SPACING_BETWEEN_PX}> + <Text fontSize="20px" fontWeight={700} fontColor={ColorOption.black}> + {title} + </Text> + </Container> + )} + <Container marginBottom={SPACING_BETWEEN_PX}> + <Text fontSize="14px" fontColor={ColorOption.grey} center={true}> + {description} + </Text> + </Container> + <Container marginBottom={SPACING_BETWEEN_PX}> + {moreInfoSettings && ( + <Text + center={true} + fontSize="13px" + textDecorationLine="underline" + fontColor={ColorOption.lightGrey} + href={moreInfoSettings.href} + > + {moreInfoSettings.text} + </Text> + )} + </Container> + </Flex> + <Container>{action}</Container> + </Container> +); diff --git a/packages/instant/src/components/standard_sliding_panel.tsx b/packages/instant/src/components/standard_sliding_panel.tsx new file mode 100644 index 000000000..f587ff79a --- /dev/null +++ b/packages/instant/src/components/standard_sliding_panel.tsx @@ -0,0 +1,29 @@ +import * as React from 'react'; + +import { SlideAnimationState, StandardSlidingPanelContent, StandardSlidingPanelSettings } from '../types'; + +import { InstallWalletPanelContent } from './install_wallet_panel_content'; +import { SlidingPanel } from './sliding_panel'; + +export interface StandardSlidingPanelProps extends StandardSlidingPanelSettings { + onClose: () => void; +} + +export class StandardSlidingPanel extends React.Component<StandardSlidingPanelProps> { + public render(): React.ReactNode { + const { animationState, content, onClose } = this.props; + return ( + <SlidingPanel animationState={animationState} onClose={onClose}> + {this._getNodeForContent(content)} + </SlidingPanel> + ); + } + private readonly _getNodeForContent = (content: StandardSlidingPanelContent): React.ReactNode => { + switch (content) { + case StandardSlidingPanelContent.InstallWallet: + return <InstallWalletPanelContent />; + case StandardSlidingPanelContent.None: + return null; + } + }; +} diff --git a/packages/instant/src/components/timed_progress_bar.tsx b/packages/instant/src/components/timed_progress_bar.tsx index 59aaa33a1..8465b9cd0 100644 --- a/packages/instant/src/components/timed_progress_bar.tsx +++ b/packages/instant/src/components/timed_progress_bar.tsx @@ -2,7 +2,7 @@ import * as _ from 'lodash'; import * as React from 'react'; import { PROGRESS_FINISH_ANIMATION_TIME_MS, PROGRESS_STALL_AT_WIDTH } from '../constants'; -import { ColorOption, keyframes, styled } from '../style/theme'; +import { ColorOption, css, keyframes, styled } from '../style/theme'; import { Container } from './ui/container'; @@ -20,15 +20,11 @@ export class TimedProgressBar extends React.Component<TimedProgressBarProps, {}> private readonly _barRef = React.createRef<HTMLDivElement>(); public render(): React.ReactNode { - const timedProgressProps = this._calculateTimedProgressProps(); - return ( - <Container width="100%" backgroundColor={ColorOption.lightGrey} borderRadius="6px"> - <TimedProgress {...timedProgressProps} ref={this._barRef as any} /> - </Container> - ); + const widthAnimationSettings = this._calculateWidthAnimationSettings(); + return <ProgressBar animationSettings={widthAnimationSettings} ref={this._barRef} />; } - private _calculateTimedProgressProps(): TimedProgressProps { + private _calculateWidthAnimationSettings(): WidthAnimationSettings { if (this.props.hasEnded) { if (!this._barRef.current) { throw new Error('ended but no reference'); @@ -60,21 +56,45 @@ const expandingWidthKeyframes = (fromWidth: string, toWidth: string) => { `; }; -interface TimedProgressProps { +export interface WidthAnimationSettings { timeMs: number; fromWidth: string; toWidth: string; } -export const TimedProgress = +interface ProgressProps { + width?: string; + animationSettings?: WidthAnimationSettings; +} + +export const Progress = styled.div < - TimedProgressProps > + ProgressProps > ` && { background-color: ${props => props.theme[ColorOption.primaryColor]}; border-radius: 6px; height: 6px; - animation: ${props => expandingWidthKeyframes(props.fromWidth, props.toWidth)} - ${props => props.timeMs}ms linear 1 forwards; + ${props => (props.width ? `width: ${props.width};` : '')} + ${props => + props.animationSettings + ? css` + animation: ${expandingWidthKeyframes( + props.animationSettings.fromWidth, + props.animationSettings.toWidth, + )} + ${props.animationSettings.timeMs}ms linear 1 forwards; + ` + : ''} } `; + +export interface ProgressBarProps extends ProgressProps {} + +export const ProgressBar: React.ComponentType<ProgressBarProps & React.ClassAttributes<{}>> = React.forwardRef( + (props, ref) => ( + <Container width="100%" backgroundColor={ColorOption.lightGrey} borderRadius="6px"> + <Progress {...props} ref={ref as any} /> + </Container> + ), +); diff --git a/packages/instant/src/components/ui/button.tsx b/packages/instant/src/components/ui/button.tsx index b90221bf4..e77b1b5d1 100644 --- a/packages/instant/src/components/ui/button.tsx +++ b/packages/instant/src/components/ui/button.tsx @@ -2,6 +2,9 @@ import { darken, saturate } from 'polished'; import * as React from 'react'; import { ColorOption, styled } from '../../style/theme'; +import { util } from '../../util/util'; + +export type ButtonOnClickHandler = (event: React.MouseEvent<HTMLElement>) => void; export interface ButtonProps { backgroundColor?: ColorOption; @@ -12,15 +15,26 @@ export interface ButtonProps { padding?: string; type?: string; isDisabled?: boolean; - onClick?: (event: React.MouseEvent<HTMLElement>) => void; + href?: string; + onClick?: ButtonOnClickHandler; 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 PlainButton: React.StatelessComponent<ButtonProps> = ({ + children, + isDisabled, + onClick, + href, + type, + className, +}) => { + const computedOnClick = isDisabled ? undefined : href ? util.createOpenUrlInNewWindow(href) : onClick; + return ( + <button type={type} className={className} onClick={computedOnClick} disabled={isDisabled}> + {children} + </button> + ); +}; const darkenOnHoverAmount = 0.1; const darkenOnActiveAmount = 0.2; @@ -31,7 +45,7 @@ export const Button = styled(PlainButton)` box-sizing: border-box; font-size: ${props => props.fontSize}; font-family: 'Inter UI', sans-serif; - font-weight: 600; + font-weight: 500; color: ${props => props.fontColor && props.theme[props.fontColor]}; cursor: ${props => (props.isDisabled ? 'default' : 'pointer')}; transition: background-color, opacity 0.5s ease; @@ -64,11 +78,10 @@ export const Button = styled(PlainButton)` Button.defaultProps = { backgroundColor: ColorOption.primaryColor, - borderColor: ColorOption.primaryColor, width: 'auto', isDisabled: false, - padding: '.6em 1.2em', - fontSize: '15px', + padding: '.82em 1.2em', + fontSize: '16px', }; Button.displayName = 'Button'; diff --git a/packages/instant/src/components/ui/container.tsx b/packages/instant/src/components/ui/container.tsx index 8aa5db9e5..4dafe1386 100644 --- a/packages/instant/src/components/ui/container.tsx +++ b/packages/instant/src/components/ui/container.tsx @@ -20,7 +20,7 @@ export interface ContainerProps { marginBottom?: string; marginLeft?: string; padding?: string; - borderRadius?: string; + borderRadius?: MediaChoice; border?: string; borderColor?: ColorOption; borderTop?: string; @@ -57,7 +57,6 @@ export const Container = ${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')} @@ -70,6 +69,7 @@ export const Container = ${props => props.display && stylesForMedia<string>('display', props.display)} ${props => props.width && stylesForMedia<string>('width', props.width)} ${props => props.height && stylesForMedia<string>('height', props.height)} + ${props => props.borderRadius && stylesForMedia<string>('border-radius', props.borderRadius)} background-color: ${props => (props.backgroundColor ? props.theme[props.backgroundColor] : 'none')}; border-color: ${props => (props.borderColor ? props.theme[props.borderColor] : 'none')}; &:hover { diff --git a/packages/instant/src/components/ui/icon.tsx b/packages/instant/src/components/ui/icon.tsx index a88fa87dd..811142b5b 100644 --- a/packages/instant/src/components/ui/icon.tsx +++ b/packages/instant/src/components/ui/icon.tsx @@ -20,6 +20,7 @@ interface IconInfoMapping { success: IconInfo; chevron: IconInfo; search: IconInfo; + lock: IconInfo; } const ICONS: IconInfoMapping = { closeX: { @@ -58,6 +59,11 @@ const ICONS: IconInfoMapping = { path: 'M8.39404 5.19727C8.39404 6.96289 6.96265 8.39453 5.19702 8.39453C3.4314 8.39453 2 6.96289 2 5.19727C2 3.43164 3.4314 2 5.19702 2C6.96265 2 8.39404 3.43164 8.39404 5.19727ZM8.09668 9.51074C7.26855 10.0684 6.27075 10.3945 5.19702 10.3945C2.3269 10.3945 0 8.06738 0 5.19727C0 2.32715 2.3269 0 5.19702 0C8.06738 0 10.394 2.32715 10.394 5.19727C10.394 6.27051 10.0686 7.26855 9.51074 8.09668L13.6997 12.2861L12.2854 13.7002L8.09668 9.51074Z', }, + lock: { + viewBox: '0 0 13 16', + path: + 'M6.47619 0C3.79509 0 1.60489 2.21216 1.60489 4.92014V6.33135C0.717479 6.33135 0 7.05602 0 7.95232V14.379C0 15.2753 0.717479 16 1.60489 16H11.3475C12.2349 16 12.9524 15.2753 12.9524 14.379V7.95232C12.9524 7.05602 12.2349 6.33135 11.3475 6.33135V4.92014C11.3475 2.21216 9.1573 0 6.47619 0ZM9.6482 6.33135H3.30418V4.92014C3.30418 3.16567 4.72026 1.71633 6.47619 1.71633C8.23213 1.71633 9.6482 3.16567 9.6482 4.92014V6.33135Z', + }, }; export interface IconProps { diff --git a/packages/instant/src/components/ui/input.tsx b/packages/instant/src/components/ui/input.tsx index 2fb408db4..1ea5d8fe1 100644 --- a/packages/instant/src/components/ui/input.tsx +++ b/packages/instant/src/components/ui/input.tsx @@ -3,6 +3,7 @@ import * as React from 'react'; import { ColorOption, styled } from '../../style/theme'; export interface InputProps { + tabIndex?: number; className?: string; value?: string; width?: string; diff --git a/packages/instant/src/components/ui/text.tsx b/packages/instant/src/components/ui/text.tsx index 4fe429d25..fd14cc4d1 100644 --- a/packages/instant/src/components/ui/text.tsx +++ b/packages/instant/src/components/ui/text.tsx @@ -2,6 +2,7 @@ import { darken } from 'polished'; import * as React from 'react'; import { ColorOption, styled } from '../../style/theme'; +import { util } from '../../util/util'; export interface TextProps { fontColor?: ColorOption; @@ -20,10 +21,16 @@ export interface TextProps { onClick?: (event: React.MouseEvent<HTMLElement>) => void; noWrap?: boolean; display?: string; + href?: string; } +export const Text: React.StatelessComponent<TextProps> = ({ href, onClick, ...rest }) => { + const computedOnClick = href ? util.createOpenUrlInNewWindow(href) : onClick; + return <StyledText {...rest} onClick={computedOnClick} />; +}; + const darkenOnHoverAmount = 0.3; -export const Text = +export const StyledText = styled.div < TextProps > ` diff --git a/packages/instant/src/components/wallet_prompt.tsx b/packages/instant/src/components/wallet_prompt.tsx new file mode 100644 index 000000000..a0b3ae457 --- /dev/null +++ b/packages/instant/src/components/wallet_prompt.tsx @@ -0,0 +1,47 @@ +import * as React from 'react'; + +import { ColorOption } from '../style/theme'; + +import { Container } from './ui/container'; +import { Flex } from './ui/flex'; +import { Text } from './ui/text'; + +export interface WalletPromptProps { + image: React.ReactNode; + onClick?: () => void; + primaryColor: ColorOption; + secondaryColor: ColorOption; +} + +export const WalletPrompt: React.StatelessComponent<WalletPromptProps> = ({ + onClick, + image, + children, + secondaryColor, + primaryColor, +}) => ( + <Container + padding="14.5px" + border={`1px solid ${primaryColor}`} + backgroundColor={secondaryColor} + width="100%" + borderRadius="4px" + onClick={onClick} + cursor={onClick ? 'pointer' : undefined} + boxShadowOnHover={!!onClick} + > + <Flex> + {image} + <Container marginLeft="10px"> + <Text fontSize="16px" fontColor={primaryColor}> + {children} + </Text> + </Container> + </Flex> + </Container> +); + +WalletPrompt.defaultProps = { + primaryColor: ColorOption.darkOrange, + secondaryColor: ColorOption.lightOrange, +}; diff --git a/packages/instant/src/components/zero_ex_instant.tsx b/packages/instant/src/components/zero_ex_instant.tsx index b945f9908..2267b4dbf 100644 --- a/packages/instant/src/components/zero_ex_instant.tsx +++ b/packages/instant/src/components/zero_ex_instant.tsx @@ -1,8 +1,9 @@ import * as React from 'react'; +import { ZeroExInstantContainer } from '../components/zero_ex_instant_container'; + import { INJECTED_DIV_CLASS } from '../constants'; -import { ZeroExInstantContainer } from './zero_ex_instant_container'; import { ZeroExInstantProvider, ZeroExInstantProviderProps } from './zero_ex_instant_provider'; export type ZeroExInstantProps = ZeroExInstantProviderProps; diff --git a/packages/instant/src/components/zero_ex_instant_container.tsx b/packages/instant/src/components/zero_ex_instant_container.tsx index 5748e064e..698bfef17 100644 --- a/packages/instant/src/components/zero_ex_instant_container.tsx +++ b/packages/instant/src/components/zero_ex_instant_container.tsx @@ -1,26 +1,29 @@ import * as React from 'react'; import { AvailableERC20TokenSelector } from '../containers/available_erc20_token_selector'; +import { ConnectedBuyOrderProgressOrPaymentMethod } from '../containers/connected_buy_order_progress_or_payment_method'; +import { CurrentStandardSlidingPanel } from '../containers/current_standard_sliding_panel'; import { LatestBuyQuoteOrderDetails } from '../containers/latest_buy_quote_order_details'; import { LatestError } from '../containers/latest_error'; -import { SelectedAssetBuyOrderProgress } from '../containers/selected_asset_buy_order_progress'; import { SelectedAssetBuyOrderStateButtons } from '../containers/selected_asset_buy_order_state_buttons'; import { SelectedAssetInstantHeading } from '../containers/selected_asset_instant_heading'; import { ColorOption } from '../style/theme'; import { zIndex } from '../style/z_index'; +import { OrderProcessState, SlideAnimationState } from '../types'; -import { SlideAnimationState } from './animations/slide_animation'; import { CSSReset } from './css_reset'; import { SlidingPanel } from './sliding_panel'; import { Container } from './ui/container'; import { Flex } from './ui/flex'; -export interface ZeroExInstantContainerProps {} +export interface ZeroExInstantContainerProps { + orderProcessState: OrderProcessState; +} export interface ZeroExInstantContainerState { tokenSelectionPanelAnimationState: SlideAnimationState; } -export class ZeroExInstantContainer extends React.Component<ZeroExInstantContainerProps, ZeroExInstantContainerState> { +export class ZeroExInstantContainer extends React.Component<{}, ZeroExInstantContainerState> { public state = { tokenSelectionPanelAnimationState: 'none' as SlideAnimationState, }; @@ -40,26 +43,26 @@ export class ZeroExInstantContainer extends React.Component<ZeroExInstantContain zIndex={zIndex.mainContainer} position="relative" backgroundColor={ColorOption.white} - borderRadius="3px" + borderRadius={{ default: '3px', sm: '0px' }} hasBoxShadow={true} overflow="hidden" height="100%" > <Flex direction="column" justify="flex-start" height="100%"> <SelectedAssetInstantHeading onSelectAssetClick={this._handleSymbolClick} /> - <SelectedAssetBuyOrderProgress /> + <ConnectedBuyOrderProgressOrPaymentMethod /> <LatestBuyQuoteOrderDetails /> <Container padding="20px" width="100%"> <SelectedAssetBuyOrderStateButtons /> </Container> </Flex> <SlidingPanel - title="Select Token" animationState={this.state.tokenSelectionPanelAnimationState} onClose={this._handlePanelClose} > <AvailableERC20TokenSelector onTokenSelect={this._handlePanelClose} /> </SlidingPanel> + <CurrentStandardSlidingPanel /> </Container> </Container> </React.Fragment> diff --git a/packages/instant/src/components/zero_ex_instant_overlay.tsx b/packages/instant/src/components/zero_ex_instant_overlay.tsx index 10438ab7a..2856ea3e3 100644 --- a/packages/instant/src/components/zero_ex_instant_overlay.tsx +++ b/packages/instant/src/components/zero_ex_instant_overlay.tsx @@ -1,12 +1,12 @@ import * as React from 'react'; +import { ZeroExInstantContainer } from '../components/zero_ex_instant_container'; import { ColorOption } from '../style/theme'; import { Container } from './ui/container'; import { Flex } from './ui/flex'; import { Icon } from './ui/icon'; import { Overlay } from './ui/overlay'; -import { ZeroExInstantContainer } from './zero_ex_instant_container'; import { ZeroExInstantProvider, ZeroExInstantProviderProps } from './zero_ex_instant_provider'; export interface ZeroExInstantOverlayProps extends ZeroExInstantProviderProps { diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx index 411f118cc..8be53ee20 100644 --- a/packages/instant/src/components/zero_ex_instant_provider.tsx +++ b/packages/instant/src/components/zero_ex_instant_provider.tsx @@ -73,7 +73,7 @@ export class ZeroExInstantProvider extends React.Component<ZeroExInstantProvider completeAssetMetaDataMap, networkId, ), - selectedAssetAmount: _.isUndefined(props.defaultAssetBuyAmount) + selectedAssetUnitAmount: _.isUndefined(props.defaultAssetBuyAmount) ? undefined : new BigNumber(props.defaultAssetBuyAmount), availableAssets: _.isUndefined(props.availableAssetDatas) @@ -91,12 +91,13 @@ export class ZeroExInstantProvider extends React.Component<ZeroExInstantProvider } public componentDidMount(): void { const state = this._store.getState(); + const dispatch = this._store.dispatch; // tslint:disable-next-line:no-floating-promises - asyncData.fetchEthPriceAndDispatchToStore(this._store); + asyncData.fetchEthPriceAndDispatchToStore(dispatch); // fetch available assets if none are specified if (_.isUndefined(state.availableAssets)) { // tslint:disable-next-line:no-floating-promises - asyncData.fetchAvailableAssetDatasAndDispatchToStore(this._store); + asyncData.fetchAvailableAssetDatasAndDispatchToStore(state, dispatch); } if (state.providerState.account.state !== AccountState.None) { this._accountUpdateHeartbeat = generateAccountHeartbeater({ @@ -111,8 +112,9 @@ export class ZeroExInstantProvider extends React.Component<ZeroExInstantProvider shouldPerformImmediatelyOnStart: false, }); this._buyQuoteHeartbeat.start(BUY_QUOTE_UPDATE_INTERVAL_TIME_MS); + // Trigger first buyquote fetch // tslint:disable-next-line:no-floating-promises - asyncData.fetchCurrentBuyQuoteAndDispatchToStore({ store: this._store, shouldSetPending: true }); + asyncData.fetchCurrentBuyQuoteAndDispatchToStore(state, dispatch, { updateSilently: false }); // warm up the gas price estimator cache just in case we can't // grab the gas price estimate when submitting the transaction // tslint:disable-next-line:no-floating-promises diff --git a/packages/instant/src/constants.ts b/packages/instant/src/constants.ts index 110a8248a..5bd2349b3 100644 --- a/packages/instant/src/constants.ts +++ b/packages/instant/src/constants.ts @@ -1,6 +1,6 @@ import { BigNumber } from '@0x/utils'; -import { AccountNotReady, AccountState, Network } from './types'; +import { AccountNotReady, AccountState, Network, ProviderType } from './types'; export const BIG_NUMBER_ZERO = new BigNumber(0); export const ETH_DECIMALS = 18; @@ -19,6 +19,14 @@ export const ETH_GAS_STATION_API_BASE_URL = 'https://ethgasstation.info'; export const COINBASE_API_BASE_URL = 'https://api.coinbase.com/v2'; export const PROGRESS_STALL_AT_WIDTH = '95%'; export const PROGRESS_FINISH_ANIMATION_TIME_MS = 200; +export const COINBASE_WALLET_IOS_APP_STORE_URL = 'https://itunes.apple.com/us/app/coinbase-wallet/id1278383455?mt=8'; +export const COINBASE_WALLET_ANDROID_APP_STORE_URL = 'https://play.google.com/store/apps/details?id=org.toshi&hl=en'; +export const COINBASE_WALLET_SITE_URL = 'https://wallet.coinbase.com/'; +export const META_MASK_FIREFOX_STORE_URL = 'https://addons.mozilla.org/en-US/firefox/addon/ether-metamask/'; +export const META_MASK_CHROME_STORE_URL = + 'https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn?hl=en'; +export const META_MASK_OPERA_STORE_URL = 'https://addons.opera.com/en/extensions/details/metamask/'; +export const META_MASK_SITE_URL = 'https://metamask.io/'; export const ETHEREUM_NODE_URL_BY_NETWORK = { [Network.Mainnet]: 'https://mainnet.infura.io/', [Network.Kovan]: 'https://kovan.infura.io/', @@ -33,3 +41,10 @@ export const LOADING_ACCOUNT: AccountNotReady = { export const LOCKED_ACCOUNT: AccountNotReady = { state: AccountState.Locked, }; +export const PROVIDER_TYPE_TO_NAME: { [key in ProviderType]: string } = { + [ProviderType.Cipher]: 'Cipher', + [ProviderType.MetaMask]: 'MetaMask', + [ProviderType.Mist]: 'Mist', + [ProviderType.CoinbaseWallet]: 'Coinbase Wallet', + [ProviderType.Parity]: 'Parity', +}; diff --git a/packages/instant/src/containers/connected_account_payment_method.ts b/packages/instant/src/containers/connected_account_payment_method.ts new file mode 100644 index 000000000..eacbadfca --- /dev/null +++ b/packages/instant/src/containers/connected_account_payment_method.ts @@ -0,0 +1,83 @@ +import * as React from 'react'; +import { connect } from 'react-redux'; +import { Dispatch } from 'redux'; + +import { PaymentMethod, PaymentMethodProps } from '../components/payment_method'; +import { + COINBASE_WALLET_ANDROID_APP_STORE_URL, + COINBASE_WALLET_IOS_APP_STORE_URL, + COINBASE_WALLET_SITE_URL, +} from '../constants'; +import { Action, actions } from '../redux/actions'; +import { asyncData } from '../redux/async_data'; +import { State } from '../redux/reducer'; +import { Network, Omit, OperatingSystem, ProviderState, StandardSlidingPanelContent } from '../types'; +import { envUtil } from '../util/env'; + +export interface ConnectedAccountPaymentMethodProps {} + +interface ConnectedState { + network: Network; + providerState: ProviderState; +} + +interface ConnectedDispatch { + openInstallWalletPanel: () => void; + unlockWalletAndDispatchToStore: (providerState: ProviderState) => void; +} + +type ConnectedProps = Omit<PaymentMethodProps, keyof ConnectedAccountPaymentMethodProps>; + +type FinalProps = ConnectedProps & ConnectedAccountPaymentMethodProps; + +const mapStateToProps = (state: State, _ownProps: ConnectedAccountPaymentMethodProps): ConnectedState => ({ + network: state.network, + providerState: state.providerState, +}); + +const mapDispatchToProps = ( + dispatch: Dispatch<Action>, + ownProps: ConnectedAccountPaymentMethodProps, +): ConnectedDispatch => ({ + openInstallWalletPanel: () => dispatch(actions.openStandardSlidingPanel(StandardSlidingPanelContent.InstallWallet)), + unlockWalletAndDispatchToStore: async (providerState: ProviderState) => + asyncData.fetchAccountInfoAndDispatchToStore(providerState, dispatch, true), +}); + +const mergeProps = ( + connectedState: ConnectedState, + connectedDispatch: ConnectedDispatch, + ownProps: ConnectedAccountPaymentMethodProps, +): FinalProps => ({ + ...ownProps, + network: connectedState.network, + account: connectedState.providerState.account, + walletName: connectedState.providerState.name, + onUnlockWalletClick: () => connectedDispatch.unlockWalletAndDispatchToStore(connectedState.providerState), + onInstallWalletClick: () => { + const isMobile = envUtil.isMobileOperatingSystem(); + if (!isMobile) { + connectedDispatch.openInstallWalletPanel(); + return; + } + const operatingSystem = envUtil.getOperatingSystem(); + let url = COINBASE_WALLET_SITE_URL; + switch (operatingSystem) { + case OperatingSystem.Android: + url = COINBASE_WALLET_ANDROID_APP_STORE_URL; + break; + case OperatingSystem.iOS: + url = COINBASE_WALLET_IOS_APP_STORE_URL; + break; + default: + break; + } + window.open(url, '_blank'); + }, +}); + +export const ConnectedAccountPaymentMethod: React.ComponentClass<ConnectedAccountPaymentMethodProps> = connect( + mapStateToProps, + mapDispatchToProps, + mergeProps, +)(PaymentMethod); diff --git a/packages/instant/src/containers/connected_buy_order_progress_or_payment_method.tsx b/packages/instant/src/containers/connected_buy_order_progress_or_payment_method.tsx new file mode 100644 index 000000000..cace18e7e --- /dev/null +++ b/packages/instant/src/containers/connected_buy_order_progress_or_payment_method.tsx @@ -0,0 +1,35 @@ +import * as React from 'react'; +import { connect } from 'react-redux'; + +import { State } from '../redux/reducer'; +import { OrderProcessState } from '../types'; + +import { ConnectedAccountPaymentMethod } from './connected_account_payment_method'; +import { SelectedAssetBuyOrderProgress } from './selected_asset_buy_order_progress'; + +interface BuyOrderProgressOrPaymentMethodProps { + orderProcessState: OrderProcessState; +} +export const BuyOrderProgressOrPaymentMethod = (props: BuyOrderProgressOrPaymentMethodProps) => { + const { orderProcessState } = props; + if ( + orderProcessState === OrderProcessState.Processing || + orderProcessState === OrderProcessState.Success || + orderProcessState === OrderProcessState.Failure + ) { + return <SelectedAssetBuyOrderProgress />; + } else { + return <ConnectedAccountPaymentMethod />; + } + return null; +}; + +interface ConnectedState extends BuyOrderProgressOrPaymentMethodProps {} + +export interface ConnectedBuyOrderProgressOrPaymentMethodProps {} +const mapStateToProps = (state: State, _ownProps: ConnectedBuyOrderProgressOrPaymentMethodProps): ConnectedState => ({ + orderProcessState: state.buyOrderState.processState, +}); +export const ConnectedBuyOrderProgressOrPaymentMethod: React.ComponentClass< + ConnectedBuyOrderProgressOrPaymentMethodProps +> = connect(mapStateToProps)(BuyOrderProgressOrPaymentMethod); diff --git a/packages/instant/src/containers/current_standard_sliding_panel.ts b/packages/instant/src/containers/current_standard_sliding_panel.ts new file mode 100644 index 000000000..82ac7fa1b --- /dev/null +++ b/packages/instant/src/containers/current_standard_sliding_panel.ts @@ -0,0 +1,31 @@ +import * as React from 'react'; +import { connect } from 'react-redux'; +import { Dispatch } from 'redux'; + +import { StandardSlidingPanel } from '../components/standard_sliding_panel'; +import { Action, actions } from '../redux/actions'; +import { State } from '../redux/reducer'; +import { StandardSlidingPanelSettings } from '../types'; + +export interface CurrentStandardSlidingPanelProps {} + +interface ConnectedState extends StandardSlidingPanelSettings {} + +interface ConnectedDispatch { + onClose: () => void; +} + +const mapStateToProps = (state: State, _ownProps: CurrentStandardSlidingPanelProps): ConnectedState => + state.standardSlidingPanelSettings; + +const mapDispatchToProps = ( + dispatch: Dispatch<Action>, + ownProps: CurrentStandardSlidingPanelProps, +): ConnectedDispatch => ({ + onClose: () => dispatch(actions.closeStandardSlidingPanel()), +}); + +export const CurrentStandardSlidingPanel: React.ComponentClass<CurrentStandardSlidingPanelProps> = connect( + mapStateToProps, + mapDispatchToProps, +)(StandardSlidingPanel); diff --git a/packages/instant/src/containers/latest_buy_quote_order_details.ts b/packages/instant/src/containers/latest_buy_quote_order_details.ts index 2b59ed3ae..5dfe535e7 100644 --- a/packages/instant/src/containers/latest_buy_quote_order_details.ts +++ b/packages/instant/src/containers/latest_buy_quote_order_details.ts @@ -14,6 +14,7 @@ export interface LatestBuyQuoteOrderDetailsProps {} interface ConnectedState { buyQuoteInfo?: BuyQuoteInfo; + selectedAssetUnitAmount?: BigNumber; ethUsdPrice?: BigNumber; isLoading: boolean; } @@ -21,6 +22,7 @@ interface ConnectedState { const mapStateToProps = (state: State, _ownProps: LatestBuyQuoteOrderDetailsProps): ConnectedState => ({ // use the worst case quote info buyQuoteInfo: oc(state).latestBuyQuote.worstCaseQuoteInfo(), + selectedAssetUnitAmount: state.selectedAssetUnitAmount, ethUsdPrice: state.ethUsdPrice, isLoading: state.quoteRequestState === AsyncProcessState.Pending, }); diff --git a/packages/instant/src/containers/latest_error.tsx b/packages/instant/src/containers/latest_error.tsx index c0da181f1..b7cfdb504 100644 --- a/packages/instant/src/containers/latest_error.tsx +++ b/packages/instant/src/containers/latest_error.tsx @@ -3,7 +3,6 @@ import * as React from 'react'; import { connect } from 'react-redux'; import { Dispatch } from 'redux'; -import { SlideAnimationState } from '../components/animations/slide_animation'; import { SlidingError } from '../components/sliding_error'; import { Overlay } from '../components/ui/overlay'; import { Action } from '../redux/actions'; @@ -11,7 +10,7 @@ import { State } from '../redux/reducer'; import { ScreenWidths } from '../style/media'; import { generateOverlayBlack } from '../style/theme'; import { zIndex } from '../style/z_index'; -import { Asset, DisplayStatus, Omit } from '../types'; +import { Asset, DisplayStatus, Omit, SlideAnimationState } from '../types'; import { errorFlasher } from '../util/error_flasher'; export interface LatestErrorComponentProps { diff --git a/packages/instant/src/containers/selected_asset_instant_heading.ts b/packages/instant/src/containers/selected_asset_instant_heading.ts index a407279e6..8dc127e1d 100644 --- a/packages/instant/src/containers/selected_asset_instant_heading.ts +++ b/packages/instant/src/containers/selected_asset_instant_heading.ts @@ -14,16 +14,16 @@ export interface InstantHeadingProps { } interface ConnectedState { - selectedAssetAmount?: BigNumber; - totalEthBaseAmount?: BigNumber; + selectedAssetUnitAmount?: BigNumber; + totalEthBaseUnitAmount?: BigNumber; ethUsdPrice?: BigNumber; quoteRequestState: AsyncProcessState; buyOrderState: OrderState; } const mapStateToProps = (state: State, _ownProps: InstantHeadingProps): ConnectedState => ({ - selectedAssetAmount: state.selectedAssetAmount, - totalEthBaseAmount: oc(state).latestBuyQuote.worstCaseQuoteInfo.totalEthAmount(), + selectedAssetUnitAmount: state.selectedAssetUnitAmount, + totalEthBaseUnitAmount: oc(state).latestBuyQuote.worstCaseQuoteInfo.totalEthAmount(), ethUsdPrice: state.ethUsdPrice, quoteRequestState: state.quoteRequestState, buyOrderState: state.buyOrderState, diff --git a/packages/instant/src/containers/selected_erc20_asset_amount_input.ts b/packages/instant/src/containers/selected_erc20_asset_amount_input.ts index 93ff3db70..2c2661e1a 100644 --- a/packages/instant/src/containers/selected_erc20_asset_amount_input.ts +++ b/packages/instant/src/containers/selected_erc20_asset_amount_input.ts @@ -6,11 +6,11 @@ import * as React from 'react'; import { connect } from 'react-redux'; import { Dispatch } from 'redux'; -import { ERC20AssetAmountInput } from '../components/erc20_asset_amount_input'; +import { ERC20AssetAmountInput, ERC20AssetAmountInputProps } from '../components/erc20_asset_amount_input'; import { Action, actions } from '../redux/actions'; import { State } from '../redux/reducer'; import { ColorOption } from '../style/theme'; -import { AffiliateInfo, ERC20Asset, OrderProcessState } from '../types'; +import { AffiliateInfo, ERC20Asset, Omit, OrderProcessState } from '../types'; import { buyQuoteUpdater } from '../util/buy_quote_updater'; export interface SelectedERC20AssetAmountInputProps { @@ -37,13 +37,7 @@ interface ConnectedDispatch { ) => void; } -interface ConnectedProps { - value?: BigNumber; - asset?: ERC20Asset; - onChange: (value?: BigNumber, asset?: ERC20Asset) => void; - isDisabled: boolean; - numberOfAssetsAvailable?: number; -} +type ConnectedProps = Omit<ERC20AssetAmountInputProps, keyof SelectedERC20AssetAmountInputProps>; type FinalProps = ConnectedProps & SelectedERC20AssetAmountInputProps; @@ -59,7 +53,7 @@ const mapStateToProps = (state: State, _ownProps: SelectedERC20AssetAmountInputP const assetBuyer = state.providerState.assetBuyer; return { assetBuyer, - value: state.selectedAssetAmount, + value: state.selectedAssetUnitAmount, asset: selectedAsset, isDisabled, numberOfAssetsAvailable, @@ -87,7 +81,11 @@ const mapDispatchToProps = ( // even if it's debounced, give them the illusion it's loading dispatch(actions.setQuoteRequestStatePending()); // tslint:disable-next-line:no-floating-promises - debouncedUpdateBuyQuoteAsync(assetBuyer, dispatch, asset, value, true, affiliateInfo); + debouncedUpdateBuyQuoteAsync(assetBuyer, dispatch, asset, value, { + setPending: true, + dispatchErrors: true, + affiliateInfo, + }); } }, }); diff --git a/packages/instant/src/redux/actions.ts b/packages/instant/src/redux/actions.ts index 8947c6c97..77e3dec12 100644 --- a/packages/instant/src/redux/actions.ts +++ b/packages/instant/src/redux/actions.ts @@ -2,7 +2,7 @@ import { BuyQuote } from '@0x/asset-buyer'; import { BigNumber } from '@0x/utils'; import * as _ from 'lodash'; -import { ActionsUnion, AddressAndEthBalanceInWei, Asset } from '../types'; +import { ActionsUnion, AddressAndEthBalanceInWei, Asset, StandardSlidingPanelContent } from '../types'; export interface PlainAction<T extends string> { type: T; @@ -26,7 +26,7 @@ export enum ActionTypes { SET_ACCOUNT_STATE_READY = 'SET_ACCOUNT_STATE_READY', UPDATE_ACCOUNT_ETH_BALANCE = 'UPDATE_ACCOUNT_ETH_BALANCE', UPDATE_ETH_USD_PRICE = 'UPDATE_ETH_USD_PRICE', - UPDATE_SELECTED_ASSET_AMOUNT = 'UPDATE_SELECTED_ASSET_AMOUNT', + UPDATE_SELECTED_ASSET_UNIT_AMOUNT = 'UPDATE_SELECTED_ASSET_UNIT_AMOUNT', SET_BUY_ORDER_STATE_NONE = 'SET_BUY_ORDER_STATE_NONE', SET_BUY_ORDER_STATE_VALIDATING = 'SET_BUY_ORDER_STATE_VALIDATING', SET_BUY_ORDER_STATE_PROCESSING = 'SET_BUY_ORDER_STATE_PROCESSING', @@ -41,6 +41,8 @@ export enum ActionTypes { HIDE_ERROR = 'HIDE_ERROR', CLEAR_ERROR = 'CLEAR_ERROR', RESET_AMOUNT = 'RESET_AMOUNT', + OPEN_STANDARD_SLIDING_PANEL = 'OPEN_STANDARD_SLIDING_PANEL', + CLOSE_STANDARD_SLIDING_PANEL = 'CLOSE_STANDARD_SLIDING_PANEL', } export const actions = { @@ -50,7 +52,8 @@ export const actions = { updateAccountEthBalance: (addressAndBalance: AddressAndEthBalanceInWei) => createAction(ActionTypes.UPDATE_ACCOUNT_ETH_BALANCE, addressAndBalance), updateEthUsdPrice: (price?: BigNumber) => createAction(ActionTypes.UPDATE_ETH_USD_PRICE, price), - updateSelectedAssetAmount: (amount?: BigNumber) => createAction(ActionTypes.UPDATE_SELECTED_ASSET_AMOUNT, amount), + updateSelectedAssetAmount: (amount?: BigNumber) => + createAction(ActionTypes.UPDATE_SELECTED_ASSET_UNIT_AMOUNT, amount), setBuyOrderStateNone: () => createAction(ActionTypes.SET_BUY_ORDER_STATE_NONE), setBuyOrderStateValidating: () => createAction(ActionTypes.SET_BUY_ORDER_STATE_VALIDATING), setBuyOrderStateProcessing: (txHash: string, startTimeUnix: number, expectedEndTimeUnix: number) => @@ -66,4 +69,7 @@ export const actions = { hideError: () => createAction(ActionTypes.HIDE_ERROR), clearError: () => createAction(ActionTypes.CLEAR_ERROR), resetAmount: () => createAction(ActionTypes.RESET_AMOUNT), + openStandardSlidingPanel: (content: StandardSlidingPanelContent) => + createAction(ActionTypes.OPEN_STANDARD_SLIDING_PANEL, content), + closeStandardSlidingPanel: () => createAction(ActionTypes.CLOSE_STANDARD_SLIDING_PANEL), }; diff --git a/packages/instant/src/redux/async_data.ts b/packages/instant/src/redux/async_data.ts index b920ac914..5d30388b8 100644 --- a/packages/instant/src/redux/async_data.ts +++ b/packages/instant/src/redux/async_data.ts @@ -1,102 +1,103 @@ import { AssetProxyId } from '@0x/types'; +import { Web3Wrapper } from '@0x/web3-wrapper'; import * as _ from 'lodash'; +import { Dispatch } from 'redux'; import { BIG_NUMBER_ZERO } from '../constants'; -import { AccountState, ERC20Asset, OrderProcessState } from '../types'; +import { AccountState, ERC20Asset, OrderProcessState, ProviderState } from '../types'; import { assetUtils } from '../util/asset'; import { buyQuoteUpdater } from '../util/buy_quote_updater'; import { coinbaseApi } from '../util/coinbase_api'; import { errorFlasher } from '../util/error_flasher'; import { actions } from './actions'; -import { Store } from './store'; +import { State } from './reducer'; export const asyncData = { - fetchEthPriceAndDispatchToStore: async (store: Store) => { + fetchEthPriceAndDispatchToStore: async (dispatch: Dispatch) => { try { const ethUsdPrice = await coinbaseApi.getEthUsdPrice(); - store.dispatch(actions.updateEthUsdPrice(ethUsdPrice)); + dispatch(actions.updateEthUsdPrice(ethUsdPrice)); } catch (e) { const errorMessage = 'Error fetching ETH/USD price'; - errorFlasher.flashNewErrorMessage(store.dispatch, errorMessage); - store.dispatch(actions.updateEthUsdPrice(BIG_NUMBER_ZERO)); + errorFlasher.flashNewErrorMessage(dispatch, errorMessage); + dispatch(actions.updateEthUsdPrice(BIG_NUMBER_ZERO)); } }, - fetchAvailableAssetDatasAndDispatchToStore: async (store: Store) => { - const { providerState, assetMetaDataMap, network } = store.getState(); + fetchAvailableAssetDatasAndDispatchToStore: async (state: State, dispatch: Dispatch) => { + const { providerState, assetMetaDataMap, network } = state; const assetBuyer = providerState.assetBuyer; try { const assetDatas = await assetBuyer.getAvailableAssetDatasAsync(); const assets = assetUtils.createAssetsFromAssetDatas(assetDatas, assetMetaDataMap, network); - store.dispatch(actions.setAvailableAssets(assets)); + dispatch(actions.setAvailableAssets(assets)); } catch (e) { const errorMessage = 'Could not find any assets'; - errorFlasher.flashNewErrorMessage(store.dispatch, errorMessage); + errorFlasher.flashNewErrorMessage(dispatch, errorMessage); // On error, just specify that none are available - store.dispatch(actions.setAvailableAssets([])); + dispatch(actions.setAvailableAssets([])); } }, - fetchAccountInfoAndDispatchToStore: async (options: { store: Store; shouldSetToLoading: boolean }) => { - const { store, shouldSetToLoading } = options; - const { providerState } = store.getState(); + fetchAccountInfoAndDispatchToStore: async ( + providerState: ProviderState, + dispatch: Dispatch, + shouldAttemptUnlock: boolean = false, + shouldSetToLoading: boolean = false, + ) => { const web3Wrapper = providerState.web3Wrapper; const provider = providerState.provider; if (shouldSetToLoading && providerState.account.state !== AccountState.Loading) { - store.dispatch(actions.setAccountStateLoading()); + dispatch(actions.setAccountStateLoading()); } let availableAddresses: string[]; try { // TODO(bmillman): Add support at the web3Wrapper level for calling `eth_requestAccounts` instead of calling enable here const isPrivacyModeEnabled = !_.isUndefined((provider as any).enable); - availableAddresses = isPrivacyModeEnabled - ? await (provider as any).enable() - : await web3Wrapper.getAvailableAddressesAsync(); + availableAddresses = + isPrivacyModeEnabled && shouldAttemptUnlock + ? await (provider as any).enable() + : await web3Wrapper.getAvailableAddressesAsync(); } catch (e) { - store.dispatch(actions.setAccountStateLocked()); + dispatch(actions.setAccountStateLocked()); return; } if (!_.isEmpty(availableAddresses)) { const activeAddress = availableAddresses[0]; - store.dispatch(actions.setAccountStateReady(activeAddress)); + dispatch(actions.setAccountStateReady(activeAddress)); // tslint:disable-next-line:no-floating-promises - asyncData.fetchAccountBalanceAndDispatchToStore(store); + asyncData.fetchAccountBalanceAndDispatchToStore(activeAddress, providerState.web3Wrapper, dispatch); } else { - store.dispatch(actions.setAccountStateLocked()); + dispatch(actions.setAccountStateLocked()); } }, - fetchAccountBalanceAndDispatchToStore: async (store: Store) => { - const { providerState } = store.getState(); - const web3Wrapper = providerState.web3Wrapper; - const account = providerState.account; - if (account.state !== AccountState.Ready) { - return; - } + fetchAccountBalanceAndDispatchToStore: async (address: string, web3Wrapper: Web3Wrapper, dispatch: Dispatch) => { try { - const address = account.address; const ethBalanceInWei = await web3Wrapper.getBalanceInWeiAsync(address); - store.dispatch(actions.updateAccountEthBalance({ address, ethBalanceInWei })); + dispatch(actions.updateAccountEthBalance({ address, ethBalanceInWei })); } catch (e) { // leave balance as is return; } }, - fetchCurrentBuyQuoteAndDispatchToStore: async (options: { store: Store; shouldSetPending: boolean }) => { - const { store, shouldSetPending } = options; - const { buyOrderState, providerState, selectedAsset, selectedAssetAmount, affiliateInfo } = store.getState(); + fetchCurrentBuyQuoteAndDispatchToStore: async ( + state: State, + dispatch: Dispatch, + options: { updateSilently: boolean }, + ) => { + const { buyOrderState, providerState, selectedAsset, selectedAssetUnitAmount, affiliateInfo } = state; const assetBuyer = providerState.assetBuyer; if ( - !_.isUndefined(selectedAssetAmount) && + !_.isUndefined(selectedAssetUnitAmount) && !_.isUndefined(selectedAsset) && buyOrderState.processState === OrderProcessState.None && selectedAsset.metaData.assetProxyId === AssetProxyId.ERC20 ) { await buyQuoteUpdater.updateBuyQuoteAsync( assetBuyer, - store.dispatch, + dispatch, selectedAsset as ERC20Asset, - selectedAssetAmount, - shouldSetPending, - affiliateInfo, + selectedAssetUnitAmount, + { setPending: !options.updateSilently, dispatchErrors: !options.updateSilently, affiliateInfo }, ); } }, diff --git a/packages/instant/src/redux/reducer.ts b/packages/instant/src/redux/reducer.ts index ef46fdd9d..dfc2b89f3 100644 --- a/packages/instant/src/redux/reducer.ts +++ b/packages/instant/src/redux/reducer.ts @@ -19,6 +19,8 @@ import { OrderProcessState, OrderState, ProviderState, + StandardSlidingPanelContent, + StandardSlidingPanelSettings, } from '../types'; import { Action, ActionTypes } from './actions'; @@ -30,6 +32,7 @@ export interface DefaultState { buyOrderState: OrderState; latestErrorDisplayStatus: DisplayStatus; quoteRequestState: AsyncProcessState; + standardSlidingPanelSettings: StandardSlidingPanelSettings; } // State that is required but needs to be derived from the props @@ -41,7 +44,7 @@ interface PropsDerivedState { interface OptionalState { selectedAsset: Asset; availableAssets: Asset[]; - selectedAssetAmount: BigNumber; + selectedAssetUnitAmount: BigNumber; ethUsdPrice: BigNumber; latestBuyQuote: BuyQuote; latestErrorMessage: string; @@ -56,6 +59,10 @@ export const DEFAULT_STATE: DefaultState = { buyOrderState: { processState: OrderProcessState.None }, latestErrorDisplayStatus: DisplayStatus.Hidden, quoteRequestState: AsyncProcessState.None, + standardSlidingPanelSettings: { + animationState: 'none', + content: StandardSlidingPanelContent.None, + }, }; export const createReducer = (initialState: State) => { @@ -66,11 +73,19 @@ export const createReducer = (initialState: State) => { case ActionTypes.SET_ACCOUNT_STATE_LOCKED: return reduceStateWithAccount(state, LOCKED_ACCOUNT); case ActionTypes.SET_ACCOUNT_STATE_READY: { - const account: AccountReady = { + const address = action.data; + let newAccount: AccountReady = { state: AccountState.Ready, - address: action.data, + address, }; - return reduceStateWithAccount(state, account); + const currentAccount = state.providerState.account; + if (currentAccount.state === AccountState.Ready && currentAccount.address === address) { + newAccount = { + ...newAccount, + ethBalanceInWei: currentAccount.ethBalanceInWei, + }; + } + return reduceStateWithAccount(state, newAccount); } case ActionTypes.UPDATE_ACCOUNT_ETH_BALANCE: { const { address, ethBalanceInWei } = action.data; @@ -90,10 +105,10 @@ export const createReducer = (initialState: State) => { ...state, ethUsdPrice: action.data, }; - case ActionTypes.UPDATE_SELECTED_ASSET_AMOUNT: + case ActionTypes.UPDATE_SELECTED_ASSET_UNIT_AMOUNT: return { ...state, - selectedAssetAmount: action.data, + selectedAssetUnitAmount: action.data, }; case ActionTypes.UPDATE_LATEST_BUY_QUOTE: const newBuyQuoteIfExists = action.data; @@ -204,13 +219,29 @@ export const createReducer = (initialState: State) => { latestBuyQuote: undefined, quoteRequestState: AsyncProcessState.None, buyOrderState: { processState: OrderProcessState.None }, - selectedAssetAmount: undefined, + selectedAssetUnitAmount: undefined, }; case ActionTypes.SET_AVAILABLE_ASSETS: return { ...state, availableAssets: action.data, }; + case ActionTypes.OPEN_STANDARD_SLIDING_PANEL: + return { + ...state, + standardSlidingPanelSettings: { + content: action.data, + animationState: 'slidIn', + }, + }; + case ActionTypes.CLOSE_STANDARD_SLIDING_PANEL: + return { + ...state, + standardSlidingPanelSettings: { + content: state.standardSlidingPanelSettings.content, + animationState: 'slidOut', + }, + }; default: return state; } @@ -232,9 +263,9 @@ const reduceStateWithAccount = (state: State, account: Account) => { const doesBuyQuoteMatchState = (buyQuote: BuyQuote, state: State): boolean => { const selectedAssetIfExists = state.selectedAsset; - const selectedAssetAmountIfExists = state.selectedAssetAmount; + const selectedAssetUnitAmountIfExists = state.selectedAssetUnitAmount; // if no selectedAsset or selectedAssetAmount exists on the current state, return false - if (_.isUndefined(selectedAssetIfExists) || _.isUndefined(selectedAssetAmountIfExists)) { + if (_.isUndefined(selectedAssetIfExists) || _.isUndefined(selectedAssetUnitAmountIfExists)) { return false; } // if buyQuote's assetData does not match that of the current selected asset, return false @@ -246,7 +277,7 @@ const doesBuyQuoteMatchState = (buyQuote: BuyQuote, state: State): boolean => { const selectedAssetMetaData = selectedAssetIfExists.metaData; if (selectedAssetMetaData.assetProxyId === AssetProxyId.ERC20) { const selectedAssetAmountBaseUnits = Web3Wrapper.toBaseUnitAmount( - selectedAssetAmountIfExists, + selectedAssetUnitAmountIfExists, selectedAssetMetaData.decimals, ); const doesAssetAmountMatch = selectedAssetAmountBaseUnits.eq(buyQuote.assetBuyAmount); diff --git a/packages/instant/src/style/theme.ts b/packages/instant/src/style/theme.ts index 1e9f55e00..a0751286b 100644 --- a/packages/instant/src/style/theme.ts +++ b/packages/instant/src/style/theme.ts @@ -17,6 +17,8 @@ export enum ColorOption { darkOrange = 'darkOrange', green = 'green', red = 'red', + darkBlue = 'darkBlue', + lightBlue = 'lightBlue', } export const theme: Theme = { @@ -32,6 +34,8 @@ export const theme: Theme = { darkOrange: '#F2994C', green: '#3CB34F', red: '#D00000', + darkBlue: '#135df6', + lightBlue: '#F2F7FF', }; export const transparentWhite = 'rgba(255,255,255,0.3)'; diff --git a/packages/instant/src/types.ts b/packages/instant/src/types.ts index b43a82d46..67f21a396 100644 --- a/packages/instant/src/types.ts +++ b/packages/instant/src/types.ts @@ -95,6 +95,7 @@ export interface AffiliateInfo { } export interface ProviderState { + name: string; provider: Provider; assetBuyer: AssetBuyer; web3Wrapper: Web3Wrapper; @@ -125,3 +126,42 @@ export interface AddressAndEthBalanceInWei { address: string; ethBalanceInWei: BigNumber; } + +export type SlideAnimationState = 'slidIn' | 'slidOut' | 'none'; + +export enum StandardSlidingPanelContent { + None = 'NONE', + InstallWallet = 'INSTALL_WALLET', +} + +export interface StandardSlidingPanelSettings { + animationState: SlideAnimationState; + content: StandardSlidingPanelContent; +} + +export enum Browser { + Chrome = 'CHROME', + Firefox = 'FIREFOX', + Opera = 'OPERA', + Safari = 'SAFARI', + Edge = 'EDGE', + Other = 'OTHER', +} + +export enum OperatingSystem { + Android = 'ANDROID', + iOS = 'IOS', + Mac = 'MAC', + Windows = 'WINDOWS', + WindowsPhone = 'WINDOWS_PHONE', + Linux = 'LINUX', + Other = 'OTHER', +} + +export enum ProviderType { + Parity = 'PARITY', + MetaMask = 'META_MASK', + Mist = 'MIST', + CoinbaseWallet = 'COINBASE_WALLET', + Cipher = 'CIPHER', +} diff --git a/packages/instant/src/util/asset.ts b/packages/instant/src/util/asset.ts index fbfbb19f3..40560d3eb 100644 --- a/packages/instant/src/util/asset.ts +++ b/packages/instant/src/util/asset.ts @@ -80,8 +80,6 @@ export const assetUtils = { return metaData.symbol.toUpperCase(); case AssetProxyId.ERC721: return metaData.name; - default: - return defaultName; } }, formattedSymbolForAsset: (asset?: ERC20Asset, defaultName: string = '???'): string => { diff --git a/packages/instant/src/util/buy_quote_updater.ts b/packages/instant/src/util/buy_quote_updater.ts index c33e28f1c..2fd16d781 100644 --- a/packages/instant/src/util/buy_quote_updater.ts +++ b/packages/instant/src/util/buy_quote_updater.ts @@ -15,40 +15,43 @@ export const buyQuoteUpdater = { assetBuyer: AssetBuyer, dispatch: Dispatch<Action>, asset: ERC20Asset, - assetAmount: BigNumber, - setPending = true, - affiliateInfo?: AffiliateInfo, + assetUnitAmount: BigNumber, + options: { setPending: boolean; dispatchErrors: boolean; affiliateInfo?: AffiliateInfo }, ): Promise<void> => { // get a new buy quote. - const baseUnitValue = Web3Wrapper.toBaseUnitAmount(assetAmount, asset.metaData.decimals); - if (setPending) { + const baseUnitValue = Web3Wrapper.toBaseUnitAmount(assetUnitAmount, asset.metaData.decimals); + if (options.setPending) { // mark quote as pending dispatch(actions.setQuoteRequestStatePending()); } - const feePercentage = oc(affiliateInfo).feePercentage(); + const feePercentage = oc(options.affiliateInfo).feePercentage(); let newBuyQuote: BuyQuote | undefined; try { newBuyQuote = await assetBuyer.getBuyQuoteAsync(asset.assetData, baseUnitValue, { feePercentage }); } catch (error) { - dispatch(actions.setQuoteRequestStateFailure()); - let errorMessage; - if (error.message === AssetBuyerError.InsufficientAssetLiquidity) { - const assetName = assetUtils.bestNameForAsset(asset, 'of this asset'); - errorMessage = `Not enough ${assetName} available`; - } else if (error.message === AssetBuyerError.InsufficientZrxLiquidity) { - errorMessage = 'Not enough ZRX available'; - } else if ( - error.message === AssetBuyerError.StandardRelayerApiError || - error.message.startsWith(AssetBuyerError.AssetUnavailable) - ) { - const assetName = assetUtils.bestNameForAsset(asset, 'This asset'); - errorMessage = `${assetName} is currently unavailable`; - } - if (!_.isUndefined(errorMessage)) { - errorFlasher.flashNewErrorMessage(dispatch, errorMessage); - } else { - throw error; + if (options.dispatchErrors) { + dispatch(actions.setQuoteRequestStateFailure()); + let errorMessage; + if (error.message === AssetBuyerError.InsufficientAssetLiquidity) { + const assetName = assetUtils.bestNameForAsset(asset, 'of this asset'); + errorMessage = `Not enough ${assetName} available`; + } else if (error.message === AssetBuyerError.InsufficientZrxLiquidity) { + errorMessage = 'Not enough ZRX available'; + } else if ( + error.message === AssetBuyerError.StandardRelayerApiError || + error.message.startsWith(AssetBuyerError.AssetUnavailable) + ) { + const assetName = assetUtils.bestNameForAsset(asset, 'This asset'); + errorMessage = `${assetName} is currently unavailable`; + } + if (!_.isUndefined(errorMessage)) { + errorFlasher.flashNewErrorMessage(dispatch, errorMessage); + } else { + throw error; + } } + // TODO: report to error reporter on else + return; } // We have a successful new buy quote diff --git a/packages/instant/src/util/env.ts b/packages/instant/src/util/env.ts new file mode 100644 index 000000000..4a32f9cb1 --- /dev/null +++ b/packages/instant/src/util/env.ts @@ -0,0 +1,65 @@ +import * as bowser from 'bowser'; +import { Provider } from 'ethereum-types'; +import * as _ from 'lodash'; + +import { PROVIDER_TYPE_TO_NAME } from '../constants'; +import { Browser, OperatingSystem, ProviderType } from '../types'; + +export const envUtil = { + getBrowser(): Browser { + if (bowser.chrome) { + return Browser.Chrome; + } else if (bowser.firefox) { + return Browser.Firefox; + } else if (bowser.opera) { + return Browser.Opera; + } else if (bowser.msedge) { + return Browser.Edge; + } else if (bowser.safari) { + return Browser.Safari; + } else { + return Browser.Other; + } + }, + isMobileOperatingSystem(): boolean { + return bowser.mobile; + }, + getOperatingSystem(): OperatingSystem { + if (bowser.android) { + return OperatingSystem.Android; + } else if (bowser.ios) { + return OperatingSystem.iOS; + } else if (bowser.mac) { + return OperatingSystem.Mac; + } else if (bowser.windows) { + return OperatingSystem.Windows; + } else if (bowser.windowsphone) { + return OperatingSystem.WindowsPhone; + } else if (bowser.linux) { + return OperatingSystem.Linux; + } else { + return OperatingSystem.Other; + } + }, + getProviderType(provider: Provider): ProviderType | undefined { + if (provider.constructor.name === 'EthereumProvider') { + return ProviderType.Mist; + } else if ((provider as any).isParity) { + return ProviderType.Parity; + } else if ((provider as any).isMetaMask) { + return ProviderType.MetaMask; + } else if (!_.isUndefined(_.get(window, 'SOFA'))) { + return ProviderType.CoinbaseWallet; + } else if (!_.isUndefined(_.get(window, '__CIPHER__'))) { + return ProviderType.Cipher; + } + return; + }, + getProviderName(provider: Provider): string { + const providerTypeIfExists = envUtil.getProviderType(provider); + if (_.isUndefined(providerTypeIfExists)) { + return provider.constructor.name; + } + return PROVIDER_TYPE_TO_NAME[providerTypeIfExists]; + }, +}; diff --git a/packages/instant/src/util/etherscan.ts b/packages/instant/src/util/etherscan.ts index 4d62c4d9f..f9bf82827 100644 --- a/packages/instant/src/util/etherscan.ts +++ b/packages/instant/src/util/etherscan.ts @@ -8,9 +8,8 @@ const etherscanPrefix = (networkId: number): string | undefined => { return 'kovan.'; case Network.Mainnet: return ''; - default: - return undefined; } + return ''; }; export const etherscanUtil = { diff --git a/packages/instant/src/util/format.ts b/packages/instant/src/util/format.ts index 44661d697..e9c432b2f 100644 --- a/packages/instant/src/util/format.ts +++ b/packages/instant/src/util/format.ts @@ -5,15 +5,15 @@ import * as _ from 'lodash'; import { ETH_DECIMALS } from '../constants'; export const format = { - ethBaseAmount: ( - ethBaseAmount?: BigNumber, + ethBaseUnitAmount: ( + ethBaseUnitAmount?: BigNumber, decimalPlaces: number = 4, defaultText: React.ReactNode = '0 ETH', ): React.ReactNode => { - if (_.isUndefined(ethBaseAmount)) { + if (_.isUndefined(ethBaseUnitAmount)) { return defaultText; } - const ethUnitAmount = Web3Wrapper.toUnitAmount(ethBaseAmount, ETH_DECIMALS); + const ethUnitAmount = Web3Wrapper.toUnitAmount(ethBaseUnitAmount, ETH_DECIMALS); return format.ethUnitAmount(ethUnitAmount, decimalPlaces); }, ethUnitAmount: ( @@ -27,16 +27,16 @@ export const format = { const roundedAmount = ethUnitAmount.round(decimalPlaces).toDigits(decimalPlaces); return `${roundedAmount} ETH`; }, - ethBaseAmountInUsd: ( - ethBaseAmount?: BigNumber, + ethBaseUnitAmountInUsd: ( + ethBaseUnitAmount?: BigNumber, ethUsdPrice?: BigNumber, decimalPlaces: number = 2, defaultText: React.ReactNode = '$0.00', ): React.ReactNode => { - if (_.isUndefined(ethBaseAmount) || _.isUndefined(ethUsdPrice)) { + if (_.isUndefined(ethBaseUnitAmount) || _.isUndefined(ethUsdPrice)) { return defaultText; } - const ethUnitAmount = Web3Wrapper.toUnitAmount(ethBaseAmount, ETH_DECIMALS); + const ethUnitAmount = Web3Wrapper.toUnitAmount(ethBaseUnitAmount, ETH_DECIMALS); return format.ethUnitAmountInUsd(ethUnitAmount, ethUsdPrice, decimalPlaces); }, ethUnitAmountInUsd: ( diff --git a/packages/instant/src/util/heartbeater_factory.ts b/packages/instant/src/util/heartbeater_factory.ts index 96a8ac4e6..2b852fb0d 100644 --- a/packages/instant/src/util/heartbeater_factory.ts +++ b/packages/instant/src/util/heartbeater_factory.ts @@ -10,13 +10,15 @@ export interface HeartbeatFactoryOptions { export const generateAccountHeartbeater = (options: HeartbeatFactoryOptions): Heartbeater => { const { store, shouldPerformImmediatelyOnStart } = options; return new Heartbeater(async () => { - await asyncData.fetchAccountInfoAndDispatchToStore({ store, shouldSetToLoading: false }); + await asyncData.fetchAccountInfoAndDispatchToStore(store.getState().providerState, store.dispatch, false); }, shouldPerformImmediatelyOnStart); }; export const generateBuyQuoteHeartbeater = (options: HeartbeatFactoryOptions): Heartbeater => { const { store, shouldPerformImmediatelyOnStart } = options; return new Heartbeater(async () => { - await asyncData.fetchCurrentBuyQuoteAndDispatchToStore({ store, shouldSetPending: false }); + await asyncData.fetchCurrentBuyQuoteAndDispatchToStore(store.getState(), store.dispatch, { + updateSilently: true, + }); }, shouldPerformImmediatelyOnStart); }; diff --git a/packages/instant/src/util/provider_state_factory.ts b/packages/instant/src/util/provider_state_factory.ts index 3281f6bfb..452a71460 100644 --- a/packages/instant/src/util/provider_state_factory.ts +++ b/packages/instant/src/util/provider_state_factory.ts @@ -4,6 +4,7 @@ import * as _ from 'lodash'; import { LOADING_ACCOUNT, NO_ACCOUNT } from '../constants'; import { Maybe, Network, OrderSource, ProviderState } from '../types'; +import { envUtil } from '../util/env'; import { assetBuyerFactory } from './asset_buyer_factory'; import { providerFactory } from './provider_factory'; @@ -29,6 +30,7 @@ export const providerStateFactory = { provider: Provider, ): ProviderState => { const providerState: ProviderState = { + name: envUtil.getProviderName(provider), provider, web3Wrapper: new Web3Wrapper(provider), assetBuyer: assetBuyerFactory.getAssetBuyer(provider, orderSource, network), @@ -40,6 +42,7 @@ export const providerStateFactory = { const injectedProviderIfExists = providerFactory.getInjectedProviderIfExists(); if (!_.isUndefined(injectedProviderIfExists)) { const providerState: ProviderState = { + name: envUtil.getProviderName(injectedProviderIfExists), provider: injectedProviderIfExists, web3Wrapper: new Web3Wrapper(injectedProviderIfExists), assetBuyer: assetBuyerFactory.getAssetBuyer(injectedProviderIfExists, orderSource, network), @@ -53,6 +56,7 @@ export const providerStateFactory = { getInitialProviderStateFallback: (orderSource: OrderSource, network: Network): ProviderState => { const provider = providerFactory.getFallbackNoSigningProvider(network); const providerState: ProviderState = { + name: envUtil.getProviderName(provider), provider, web3Wrapper: new Web3Wrapper(provider), assetBuyer: assetBuyerFactory.getAssetBuyer(provider, orderSource, network), diff --git a/packages/instant/src/util/util.ts b/packages/instant/src/util/util.ts index 232a86850..29b6b1d2b 100644 --- a/packages/instant/src/util/util.ts +++ b/packages/instant/src/util/util.ts @@ -2,4 +2,5 @@ import * as _ from 'lodash'; export const util = { boundNoop: _.noop.bind(_), + createOpenUrlInNewWindow: (href: string) => () => window.open(href, '_blank'), }; diff --git a/packages/instant/test/util/format.test.ts b/packages/instant/test/util/format.test.ts index c346b7604..fe0a63e6e 100644 --- a/packages/instant/test/util/format.test.ts +++ b/packages/instant/test/util/format.test.ts @@ -15,20 +15,20 @@ const BIG_NUMBER_FAKE_ETH_USD_PRICE = new BigNumber(2.534); describe('format', () => { describe('ethBaseAmount', () => { it('converts 1 ETH in base units to the string `1 ETH`', () => { - expect(format.ethBaseAmount(ONE_ETH_IN_BASE_UNITS)).toBe('1 ETH'); + expect(format.ethBaseUnitAmount(ONE_ETH_IN_BASE_UNITS)).toBe('1 ETH'); }); it('converts .432414 ETH in base units to the string `.4324 ETH`', () => { - expect(format.ethBaseAmount(DECIMAL_ETH_IN_BASE_UNITS)).toBe('0.4324 ETH'); + expect(format.ethBaseUnitAmount(DECIMAL_ETH_IN_BASE_UNITS)).toBe('0.4324 ETH'); }); it('converts 5.3014059295032 ETH in base units to the string `5.301 ETH`', () => { - expect(format.ethBaseAmount(IRRATIONAL_ETH_IN_BASE_UNITS)).toBe('5.301 ETH'); + expect(format.ethBaseUnitAmount(IRRATIONAL_ETH_IN_BASE_UNITS)).toBe('5.301 ETH'); }); it('returns defaultText param when ethBaseAmount is not defined', () => { const defaultText = 'defaultText'; - expect(format.ethBaseAmount(undefined, 4, defaultText)).toBe(defaultText); + expect(format.ethBaseUnitAmount(undefined, 4, defaultText)).toBe(defaultText); }); it('it allows for configurable decimal places', () => { - expect(format.ethBaseAmount(DECIMAL_ETH_IN_BASE_UNITS, 2)).toBe('0.43 ETH'); + expect(format.ethBaseUnitAmount(DECIMAL_ETH_IN_BASE_UNITS, 2)).toBe('0.43 ETH'); }); }); describe('ethUnitAmount', () => { @@ -52,24 +52,26 @@ describe('format', () => { }); describe('ethBaseAmountInUsd', () => { it('correctly formats 1 ETH to usd according to some price', () => { - expect(format.ethBaseAmountInUsd(ONE_ETH_IN_BASE_UNITS, BIG_NUMBER_FAKE_ETH_USD_PRICE)).toBe('$2.53'); + expect(format.ethBaseUnitAmountInUsd(ONE_ETH_IN_BASE_UNITS, BIG_NUMBER_FAKE_ETH_USD_PRICE)).toBe('$2.53'); }); it('correctly formats .432414 ETH to usd according to some price', () => { - expect(format.ethBaseAmountInUsd(DECIMAL_ETH_IN_BASE_UNITS, BIG_NUMBER_FAKE_ETH_USD_PRICE)).toBe('$1.10'); + expect(format.ethBaseUnitAmountInUsd(DECIMAL_ETH_IN_BASE_UNITS, BIG_NUMBER_FAKE_ETH_USD_PRICE)).toBe( + '$1.10', + ); }); it('correctly formats 5.3014059295032 ETH to usd according to some price', () => { - expect(format.ethBaseAmountInUsd(IRRATIONAL_ETH_IN_BASE_UNITS, BIG_NUMBER_FAKE_ETH_USD_PRICE)).toBe( + expect(format.ethBaseUnitAmountInUsd(IRRATIONAL_ETH_IN_BASE_UNITS, BIG_NUMBER_FAKE_ETH_USD_PRICE)).toBe( '$13.43', ); }); it('returns defaultText param when ethBaseAmountInUsd or ethUsdPrice is not defined', () => { const defaultText = 'defaultText'; - expect(format.ethBaseAmountInUsd(undefined, undefined, 2, defaultText)).toBe(defaultText); - expect(format.ethBaseAmountInUsd(BIG_NUMBER_ONE, undefined, 2, defaultText)).toBe(defaultText); - expect(format.ethBaseAmountInUsd(undefined, BIG_NUMBER_ONE, 2, defaultText)).toBe(defaultText); + expect(format.ethBaseUnitAmountInUsd(undefined, undefined, 2, defaultText)).toBe(defaultText); + expect(format.ethBaseUnitAmountInUsd(BIG_NUMBER_ONE, undefined, 2, defaultText)).toBe(defaultText); + expect(format.ethBaseUnitAmountInUsd(undefined, BIG_NUMBER_ONE, 2, defaultText)).toBe(defaultText); }); it('it allows for configurable decimal places', () => { - expect(format.ethBaseAmountInUsd(DECIMAL_ETH_IN_BASE_UNITS, BIG_NUMBER_FAKE_ETH_USD_PRICE, 4)).toBe( + expect(format.ethBaseUnitAmountInUsd(DECIMAL_ETH_IN_BASE_UNITS, BIG_NUMBER_FAKE_ETH_USD_PRICE, 4)).toBe( '$1.0957', ); }); diff --git a/packages/instant/tslint.json b/packages/instant/tslint.json index 08b76be97..d43ee8da7 100644 --- a/packages/instant/tslint.json +++ b/packages/instant/tslint.json @@ -3,6 +3,7 @@ "rules": { "custom-no-magic-numbers": false, "semicolon": [true, "always", "ignore-bound-class-methods"], - "max-classes-per-file": false + "max-classes-per-file": false, + "switch-default": false } } diff --git a/packages/instant/webpack.config.js b/packages/instant/webpack.config.js index 78a33ce90..3129e13a6 100644 --- a/packages/instant/webpack.config.js +++ b/packages/instant/webpack.config.js @@ -1,7 +1,8 @@ const path = require('path'); +const ip = require('ip'); // The common js bundle (not this one) is built using tsc. // The umd bundle (this one) has a different entrypoint. -module.exports = { +const config = { entry: './src/index.umd.ts', output: { filename: '[name].bundle.js', @@ -24,5 +25,15 @@ module.exports = { devServer: { contentBase: path.join(__dirname, 'public'), port: 5000, + host: '0.0.0.0', + after: () => { + if (config.devServer.host === '0.0.0.0') { + console.log( + `webpack-dev-server can be accessed externally at: http://${ip.address()}:${config.devServer.port}`, + ); + } + }, }, }; + +module.exports = config; diff --git a/packages/json-schemas/CHANGELOG.json b/packages/json-schemas/CHANGELOG.json index 24eacd192..bf815e523 100644 --- a/packages/json-schemas/CHANGELOG.json +++ b/packages/json-schemas/CHANGELOG.json @@ -1,5 +1,14 @@ [ { + "timestamp": 1542208198, + "version": "2.1.1", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "version": "2.1.0", "changes": [ { diff --git a/packages/json-schemas/CHANGELOG.md b/packages/json-schemas/CHANGELOG.md index 4f77d3aee..dc412bded 100644 --- a/packages/json-schemas/CHANGELOG.md +++ b/packages/json-schemas/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.1.1 - _November 14, 2018_ + + * Dependencies updated + ## v2.1.0 - _November 12, 2018_ * Improve schemas by enforcing that amounts that must be whole numbers (e.g Order asset amounts) no longer allow decimal amounts (#1173) diff --git a/packages/json-schemas/package.json b/packages/json-schemas/package.json index 2f57f5097..fb89a2d5b 100644 --- a/packages/json-schemas/package.json +++ b/packages/json-schemas/package.json @@ -1,6 +1,6 @@ { "name": "@0x/json-schemas", - "version": "2.1.0", + "version": "2.1.1", "engines": { "node": ">=6.12" }, @@ -46,7 +46,7 @@ }, "devDependencies": { "@0x/tslint-config": "^1.0.10", - "@0x/utils": "^2.0.4", + "@0x/utils": "^2.0.5", "@types/lodash.foreach": "^4.5.3", "@types/lodash.values": "^4.3.3", "@types/mocha": "^2.2.42", diff --git a/packages/metacoin/package.json b/packages/metacoin/package.json index 250730d26..f990db6b0 100644 --- a/packages/metacoin/package.json +++ b/packages/metacoin/package.json @@ -1,6 +1,6 @@ { "name": "@0x/metacoin", - "version": "0.0.26", + "version": "0.0.28", "engines": { "node": ">=6.12" }, @@ -29,15 +29,15 @@ "author": "", "license": "Apache-2.0", "dependencies": { - "@0x/abi-gen": "^1.0.15", - "@0x/base-contract": "^3.0.4", - "@0x/sol-cov": "^2.1.10", - "@0x/subproviders": "^2.1.2", + "@0x/abi-gen": "^1.0.16", + "@0x/base-contract": "^3.0.6", + "@0x/sol-cov": "^2.1.12", + "@0x/subproviders": "^2.1.4", "@0x/tslint-config": "^1.0.10", "@0x/types": "^1.2.1", "@0x/typescript-typings": "^3.0.4", - "@0x/utils": "^2.0.4", - "@0x/web3-wrapper": "^3.1.2", + "@0x/utils": "^2.0.5", + "@0x/web3-wrapper": "^3.1.4", "@types/mocha": "^5.2.2", "copyfiles": "^2.0.0", "ethereum-types": "^1.1.2", @@ -46,8 +46,8 @@ "run-s": "^0.0.0" }, "devDependencies": { - "@0x/dev-utils": "^1.0.15", - "@0x/sol-compiler": "^1.1.10", + "@0x/dev-utils": "^1.0.17", + "@0x/sol-compiler": "^1.1.12", "chai": "^4.0.1", "chai-as-promised": "^7.1.0", "chai-bignumber": "^2.0.1", diff --git a/packages/migrations/CHANGELOG.json b/packages/migrations/CHANGELOG.json index f57139f40..5b0d58909 100644 --- a/packages/migrations/CHANGELOG.json +++ b/packages/migrations/CHANGELOG.json @@ -1,5 +1,23 @@ [ { + "timestamp": 1542208198, + "version": "2.0.4", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { + "timestamp": 1542134075, + "version": "2.0.3", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "timestamp": 1542028948, "version": "2.0.2", "changes": [ diff --git a/packages/migrations/CHANGELOG.md b/packages/migrations/CHANGELOG.md index 56c26a472..87d0b25ca 100644 --- a/packages/migrations/CHANGELOG.md +++ b/packages/migrations/CHANGELOG.md @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.0.4 - _November 14, 2018_ + + * Dependencies updated + +## v2.0.3 - _November 13, 2018_ + + * Dependencies updated + ## v2.0.2 - _November 12, 2018_ * Dependencies updated diff --git a/packages/migrations/package.json b/packages/migrations/package.json index cbab964d5..8eaae96ba 100644 --- a/packages/migrations/package.json +++ b/packages/migrations/package.json @@ -1,6 +1,6 @@ { "name": "@0x/migrations", - "version": "2.0.2", + "version": "2.0.4", "engines": { "node": ">=6.12" }, @@ -17,7 +17,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@0x/dev-utils": "^1.0.15", + "@0x/dev-utils": "^1.0.17", "@0x/tslint-config": "^1.0.10", "@0x/types": "^1.2.1", "@types/yargs": "^10.0.0", @@ -29,16 +29,16 @@ "yargs": "^10.0.3" }, "dependencies": { - "@0x/abi-gen-wrappers": "^1.0.3", - "@0x/base-contract": "^3.0.4", + "@0x/abi-gen-wrappers": "^1.0.5", + "@0x/base-contract": "^3.0.6", "@0x/contract-addresses": "^1.1.0", "@0x/contract-artifacts": "^1.1.0", - "@0x/order-utils": "^3.0.0", - "@0x/sol-compiler": "^1.1.10", - "@0x/subproviders": "^2.1.2", + "@0x/order-utils": "^3.0.2", + "@0x/sol-compiler": "^1.1.12", + "@0x/subproviders": "^2.1.4", "@0x/typescript-typings": "^3.0.4", - "@0x/utils": "^2.0.4", - "@0x/web3-wrapper": "^3.1.2", + "@0x/utils": "^2.0.5", + "@0x/web3-wrapper": "^3.1.4", "@ledgerhq/hw-app-eth": "^4.3.0", "ethereum-types": "^1.1.2", "ethers": "~4.0.4", diff --git a/packages/order-utils/CHANGELOG.json b/packages/order-utils/CHANGELOG.json index 6ce3169d5..55e7defda 100644 --- a/packages/order-utils/CHANGELOG.json +++ b/packages/order-utils/CHANGELOG.json @@ -1,5 +1,23 @@ [ { + "timestamp": 1542208198, + "version": "3.0.2", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { + "timestamp": 1542134075, + "version": "3.0.1", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "version": "3.0.0", "changes": [ { diff --git a/packages/order-utils/CHANGELOG.md b/packages/order-utils/CHANGELOG.md index eaa74d6ce..edf1569a7 100644 --- a/packages/order-utils/CHANGELOG.md +++ b/packages/order-utils/CHANGELOG.md @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v3.0.2 - _November 14, 2018_ + + * Dependencies updated + +## v3.0.1 - _November 13, 2018_ + + * Dependencies updated + ## v3.0.0 - _November 12, 2018_ * Add signature validation, regular cancellation and `cancelledUpTo` checks to `validateOrderFillableOrThrowAsync` (#1235) diff --git a/packages/order-utils/package.json b/packages/order-utils/package.json index 060150f96..fe5ea70b1 100644 --- a/packages/order-utils/package.json +++ b/packages/order-utils/package.json @@ -1,6 +1,6 @@ { "name": "@0x/order-utils", - "version": "3.0.0", + "version": "3.0.2", "engines": { "node": ">=6.12" }, @@ -35,7 +35,7 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/order-utils/README.md", "devDependencies": { - "@0x/dev-utils": "^1.0.15", + "@0x/dev-utils": "^1.0.17", "@0x/tslint-config": "^1.0.10", "@types/bn.js": "^4.11.0", "@types/lodash": "4.14.104", @@ -53,15 +53,15 @@ "typescript": "3.0.1" }, "dependencies": { - "@0x/abi-gen-wrappers": "^1.0.3", - "@0x/assert": "^1.0.16", - "@0x/base-contract": "^3.0.4", + "@0x/abi-gen-wrappers": "^1.0.5", + "@0x/assert": "^1.0.17", + "@0x/base-contract": "^3.0.6", "@0x/contract-artifacts": "^1.1.0", - "@0x/json-schemas": "^2.1.0", + "@0x/json-schemas": "^2.1.1", "@0x/types": "^1.2.1", "@0x/typescript-typings": "^3.0.4", - "@0x/utils": "^2.0.4", - "@0x/web3-wrapper": "^3.1.2", + "@0x/utils": "^2.0.5", + "@0x/web3-wrapper": "^3.1.4", "@types/node": "*", "bn.js": "^4.11.8", "ethereum-types": "^1.1.2", diff --git a/packages/order-watcher/CHANGELOG.json b/packages/order-watcher/CHANGELOG.json index dbb4824fc..b4d7a1501 100644 --- a/packages/order-watcher/CHANGELOG.json +++ b/packages/order-watcher/CHANGELOG.json @@ -1,12 +1,28 @@ [ { + "version": "2.2.4", + "changes": [ + { + "note": + "Fix the bug when order watcher was throwing an error on order removal when maker token was ZRX", + "pr": 1259 + } + ], + "timestamp": 1542208198 + }, + { "version": "2.2.3", "changes": [ { "note": "Start jsonRpcRequestId at 1, not 0 as 0 breaks the web3.js websocket RPC provider", "pr": 1227 + }, + { + "note": + "Fix the bug when order watcher was trying to convert undefined to an object in case of CancelUpTo event" } - ] + ], + "timestamp": 1542134075 }, { "timestamp": 1542028948, diff --git a/packages/order-watcher/CHANGELOG.md b/packages/order-watcher/CHANGELOG.md index 1470b0e48..c7003f759 100644 --- a/packages/order-watcher/CHANGELOG.md +++ b/packages/order-watcher/CHANGELOG.md @@ -5,6 +5,15 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.2.4 - _November 14, 2018_ + + * Fix the bug when order watcher was throwing an error on order removal when maker token was ZRX (#1259) + +## v2.2.3 - _November 13, 2018_ + + * Start jsonRpcRequestId at 1, not 0 as 0 breaks the web3.js websocket RPC provider (#1227) + * Fix the bug when order watcher was trying to convert undefined to an object in case of CancelUpTo event + ## v2.2.2 - _November 12, 2018_ * Dependencies updated diff --git a/packages/order-watcher/package.json b/packages/order-watcher/package.json index 6bb1ef485..af88a120e 100644 --- a/packages/order-watcher/package.json +++ b/packages/order-watcher/package.json @@ -1,6 +1,6 @@ { "name": "@0x/order-watcher", - "version": "2.2.2", + "version": "2.2.4", "description": "An order watcher daemon that watches for order validity", "keywords": [ "0x", @@ -33,8 +33,8 @@ "node": ">=6.0.0" }, "devDependencies": { - "@0x/dev-utils": "^1.0.15", - "@0x/migrations": "^2.0.2", + "@0x/dev-utils": "^1.0.17", + "@0x/migrations": "^2.0.4", "@0x/tslint-config": "^1.0.10", "@types/bintrees": "^1.0.2", "@types/lodash": "4.14.104", @@ -57,19 +57,19 @@ "typescript": "3.0.1" }, "dependencies": { - "@0x/abi-gen-wrappers": "^1.0.3", - "@0x/assert": "^1.0.16", - "@0x/base-contract": "^3.0.4", + "@0x/abi-gen-wrappers": "^1.0.5", + "@0x/assert": "^1.0.17", + "@0x/base-contract": "^3.0.6", "@0x/contract-addresses": "^1.1.0", "@0x/contract-artifacts": "^1.1.0", - "@0x/contract-wrappers": "^4.0.0", - "@0x/fill-scenarios": "^1.0.10", - "@0x/json-schemas": "^2.1.0", - "@0x/order-utils": "^3.0.0", + "@0x/contract-wrappers": "^4.0.2", + "@0x/fill-scenarios": "^1.0.12", + "@0x/json-schemas": "^2.1.1", + "@0x/order-utils": "^3.0.2", "@0x/types": "^1.2.1", "@0x/typescript-typings": "^3.0.4", - "@0x/utils": "^2.0.4", - "@0x/web3-wrapper": "^3.1.2", + "@0x/utils": "^2.0.5", + "@0x/web3-wrapper": "^3.1.4", "bintrees": "^1.0.2", "ethereum-types": "^1.1.2", "ethereumjs-blockstream": "6.0.0", diff --git a/packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts b/packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts index dbcc25186..a956a94db 100644 --- a/packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts +++ b/packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts @@ -50,7 +50,7 @@ export class DependentOrderHashesTracker { return uniqueOrderHashList; } public getDependentOrderHashesByMaker(makerAddress: string): string[] { - const dependentOrderHashes = Array.from(this._orderHashesByMakerAddress[makerAddress]); + const dependentOrderHashes = Array.from(this._orderHashesByMakerAddress[makerAddress] || {}); return dependentOrderHashes; } public getDependentOrderHashesByAssetDataByMaker(makerAddress: string, assetData: string): string[] { @@ -89,7 +89,10 @@ export class DependentOrderHashesTracker { (decodedMakerAssetData as ERC721AssetData).tokenId, ); } - this._removeFromERC20DependentOrderhashes(signedOrder, this._zrxTokenAddress); + // If makerToken === ZRX then we already removed it and we don't need to remove it again. + if ((decodedMakerAssetData as ERC20AssetData).tokenAddress !== this._zrxTokenAddress) { + this._removeFromERC20DependentOrderhashes(signedOrder, this._zrxTokenAddress); + } this._removeFromMakerDependentOrderhashes(signedOrder); } private _getDependentOrderHashesByERC20AssetData(makerAddress: string, erc20AssetData: string): string[] { diff --git a/packages/react-docs/CHANGELOG.json b/packages/react-docs/CHANGELOG.json index 42de578a6..0e9afc32a 100644 --- a/packages/react-docs/CHANGELOG.json +++ b/packages/react-docs/CHANGELOG.json @@ -1,5 +1,23 @@ [ { + "timestamp": 1542208198, + "version": "1.0.18", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { + "timestamp": 1542134075, + "version": "1.0.17", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "timestamp": 1542028948, "version": "1.0.16", "changes": [ diff --git a/packages/react-docs/CHANGELOG.md b/packages/react-docs/CHANGELOG.md index 7f0514ddb..db574e41b 100644 --- a/packages/react-docs/CHANGELOG.md +++ b/packages/react-docs/CHANGELOG.md @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.18 - _November 14, 2018_ + + * Dependencies updated + +## v1.0.17 - _November 13, 2018_ + + * Dependencies updated + ## v1.0.16 - _November 12, 2018_ * Dependencies updated diff --git a/packages/react-docs/package.json b/packages/react-docs/package.json index fb1d6d78c..3e77a9a07 100644 --- a/packages/react-docs/package.json +++ b/packages/react-docs/package.json @@ -1,6 +1,6 @@ { "name": "@0x/react-docs", - "version": "1.0.16", + "version": "1.0.18", "engines": { "node": ">=6.12" }, @@ -24,7 +24,7 @@ "url": "https://github.com/0xProject/0x-monorepo.git" }, "devDependencies": { - "@0x/dev-utils": "^1.0.15", + "@0x/dev-utils": "^1.0.17", "@0x/tslint-config": "^1.0.10", "@types/compare-versions": "^3.0.0", "@types/styled-components": "^4.0.0", @@ -34,9 +34,9 @@ "typescript": "3.0.1" }, "dependencies": { - "@0x/react-shared": "^1.0.19", + "@0x/react-shared": "^1.0.21", "@0x/types": "^1.2.1", - "@0x/utils": "^2.0.4", + "@0x/utils": "^2.0.5", "@types/lodash": "4.14.104", "@types/material-ui": "^0.20.0", "@types/node": "*", diff --git a/packages/react-docs/src/components/type.tsx b/packages/react-docs/src/components/type.tsx index 412b99b9d..fa3b658b4 100644 --- a/packages/react-docs/src/components/type.tsx +++ b/packages/react-docs/src/components/type.tsx @@ -1,9 +1,8 @@ -import { colors, constants as sharedConstants, Link, utils as sharedUtils } from '@0x/react-shared'; +import { colors, Link, utils as sharedUtils } from '@0x/react-shared'; import { Type as TypeDef, TypeDefinitionByName, TypeDocTypes } from '@0x/types'; import { errorUtils } from '@0x/utils'; import * as _ from 'lodash'; import * as React from 'react'; -import { Link as ScrollLink } from 'react-scroll'; import * as ReactTooltip from 'react-tooltip'; import { DocsInfo } from '../docs_info'; @@ -224,13 +223,7 @@ export const Type: React.SFC<TypeProps> = (props: TypeProps): any => { {sharedUtils.isUserOnMobile() || props.isInPopover || isExportedClassReference ? ( <span style={{ color: colors.lightBlueA700, cursor: 'pointer' }}>{typeName}</span> ) : ( - <ScrollLink - to={typeDefinitionAnchorId} - offset={0} - hashSpy={true} - duration={sharedConstants.DOCS_SCROLL_DURATION_MS} - containerId={sharedConstants.SCROLL_CONTAINER_ID} - > + <Link to={typeDefinitionAnchorId}> <span data-tip={true} data-for={id} @@ -252,7 +245,7 @@ export const Type: React.SFC<TypeProps> = (props: TypeProps): any => { /> </ReactTooltip> </span> - </ScrollLink> + </Link> )} </span> ); diff --git a/packages/react-docs/src/docs_info.ts b/packages/react-docs/src/docs_info.ts index 54b59ef1f..76f7784ba 100644 --- a/packages/react-docs/src/docs_info.ts +++ b/packages/react-docs/src/docs_info.ts @@ -10,6 +10,7 @@ import { SectionsMap, SupportedDocJson, } from './types'; +import { constants } from './utils/constants'; export class DocsInfo { public id: string; @@ -64,7 +65,7 @@ export class DocsInfo { const docSections = _.keys(this.sections); _.each(docSections, sectionName => { const docSection = docAgnosticFormat[sectionName]; - if (_.isUndefined(docSection)) { + if (_.isUndefined(docSection) || sectionName === constants.EXTERNAL_EXPORTS_SECTION_NAME) { return; // no-op } diff --git a/packages/react-shared/CHANGELOG.json b/packages/react-shared/CHANGELOG.json index 4394eeb2e..cb8ae1bf0 100644 --- a/packages/react-shared/CHANGELOG.json +++ b/packages/react-shared/CHANGELOG.json @@ -1,5 +1,23 @@ [ { + "timestamp": 1542208198, + "version": "1.0.21", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { + "timestamp": 1542134075, + "version": "1.0.20", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "timestamp": 1542028948, "version": "1.0.19", "changes": [ diff --git a/packages/react-shared/CHANGELOG.md b/packages/react-shared/CHANGELOG.md index 4104628bc..0ecb66a27 100644 --- a/packages/react-shared/CHANGELOG.md +++ b/packages/react-shared/CHANGELOG.md @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.21 - _November 14, 2018_ + + * Dependencies updated + +## v1.0.20 - _November 13, 2018_ + + * Dependencies updated + ## v1.0.19 - _November 12, 2018_ * Dependencies updated diff --git a/packages/react-shared/package.json b/packages/react-shared/package.json index 1336ce2dd..d2a00ca9c 100644 --- a/packages/react-shared/package.json +++ b/packages/react-shared/package.json @@ -1,6 +1,6 @@ { "name": "@0x/react-shared", - "version": "1.0.19", + "version": "1.0.21", "engines": { "node": ">=6.12" }, @@ -25,7 +25,7 @@ "url": "https://github.com/0xProject/0x-monorepo.git" }, "devDependencies": { - "@0x/dev-utils": "^1.0.15", + "@0x/dev-utils": "^1.0.17", "@0x/tslint-config": "^1.0.10", "make-promises-safe": "^1.1.0", "shx": "^0.2.2", diff --git a/packages/react-shared/src/components/anchor_title.tsx b/packages/react-shared/src/components/anchor_title.tsx index bd99edcab..a9105e132 100644 --- a/packages/react-shared/src/components/anchor_title.tsx +++ b/packages/react-shared/src/components/anchor_title.tsx @@ -1,15 +1,9 @@ import * as React from 'react'; -import { Link as ScrollLink } from 'react-scroll'; import styled from 'styled-components'; +import { Link } from '../components/link'; import { HeaderSizes, Styles } from '../types'; import { colors } from '../utils/colors'; -import { constants } from '../utils/constants'; - -const headerSizeToScrollOffset: { [headerSize: string]: number } = { - h2: -20, - h3: 0, -}; export interface AnchorTitleProps { title: string | React.ReactNode; @@ -73,15 +67,9 @@ export class AnchorTitle extends React.Component<AnchorTitleProps, AnchorTitleSt {this.props.title} </div> {!this.props.isDisabled && ( - <ScrollLink - to={this.props.id} - hashSpy={true} - offset={headerSizeToScrollOffset[this.props.headerSize]} - duration={constants.DOCS_SCROLL_DURATION_MS} - containerId={constants.SCROLL_CONTAINER_ID} - > + <Link to={this.props.id}> <AnchorIcon className="zmdi zmdi-link" shouldShowAnchor={this.props.shouldShowAnchor} /> - </ScrollLink> + </Link> )} </div> ); diff --git a/packages/sol-compiler/CHANGELOG.json b/packages/sol-compiler/CHANGELOG.json index b432d9d90..fc39106dc 100644 --- a/packages/sol-compiler/CHANGELOG.json +++ b/packages/sol-compiler/CHANGELOG.json @@ -1,5 +1,23 @@ [ { + "timestamp": 1542208198, + "version": "1.1.12", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { + "timestamp": 1542134075, + "version": "1.1.11", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "timestamp": 1542028948, "version": "1.1.10", "changes": [ diff --git a/packages/sol-compiler/CHANGELOG.md b/packages/sol-compiler/CHANGELOG.md index 2e2d810a6..b69976cdc 100644 --- a/packages/sol-compiler/CHANGELOG.md +++ b/packages/sol-compiler/CHANGELOG.md @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.1.12 - _November 14, 2018_ + + * Dependencies updated + +## v1.1.11 - _November 13, 2018_ + + * Dependencies updated + ## v1.1.10 - _November 12, 2018_ * Dependencies updated diff --git a/packages/sol-compiler/package.json b/packages/sol-compiler/package.json index 7c27dbc19..2904cfd23 100644 --- a/packages/sol-compiler/package.json +++ b/packages/sol-compiler/package.json @@ -1,6 +1,6 @@ { "name": "@0x/sol-compiler", - "version": "1.1.10", + "version": "1.1.12", "engines": { "node": ">=6.12" }, @@ -42,7 +42,7 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/sol-compiler/README.md", "devDependencies": { - "@0x/dev-utils": "^1.0.15", + "@0x/dev-utils": "^1.0.17", "@0x/tslint-config": "^1.0.10", "@types/mkdirp": "^0.5.2", "@types/require-from-string": "^1.2.0", @@ -65,13 +65,13 @@ "zeppelin-solidity": "1.8.0" }, "dependencies": { - "@0x/assert": "^1.0.16", - "@0x/json-schemas": "^2.1.0", + "@0x/assert": "^1.0.17", + "@0x/json-schemas": "^2.1.1", "@0x/sol-resolver": "^1.0.16", "@0x/types": "^1.2.1", "@0x/typescript-typings": "^3.0.4", - "@0x/utils": "^2.0.4", - "@0x/web3-wrapper": "^3.1.2", + "@0x/utils": "^2.0.5", + "@0x/web3-wrapper": "^3.1.4", "@types/yargs": "^11.0.0", "chalk": "^2.3.0", "ethereum-types": "^1.1.2", diff --git a/packages/sol-cov/CHANGELOG.json b/packages/sol-cov/CHANGELOG.json index 83efa8ac7..bdeda8a96 100644 --- a/packages/sol-cov/CHANGELOG.json +++ b/packages/sol-cov/CHANGELOG.json @@ -1,5 +1,23 @@ [ { + "timestamp": 1542208198, + "version": "2.1.12", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { + "timestamp": 1542134075, + "version": "2.1.11", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "timestamp": 1542028948, "version": "2.1.10", "changes": [ diff --git a/packages/sol-cov/CHANGELOG.md b/packages/sol-cov/CHANGELOG.md index 50ca2d6e2..906c0dd52 100644 --- a/packages/sol-cov/CHANGELOG.md +++ b/packages/sol-cov/CHANGELOG.md @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.1.12 - _November 14, 2018_ + + * Dependencies updated + +## v2.1.11 - _November 13, 2018_ + + * Dependencies updated + ## v2.1.10 - _November 12, 2018_ * Dependencies updated diff --git a/packages/sol-cov/package.json b/packages/sol-cov/package.json index 613bce8d3..159a2207d 100644 --- a/packages/sol-cov/package.json +++ b/packages/sol-cov/package.json @@ -1,6 +1,6 @@ { "name": "@0x/sol-cov", - "version": "2.1.10", + "version": "2.1.12", "engines": { "node": ">=6.12" }, @@ -42,12 +42,12 @@ }, "homepage": "https://github.com/0xProject/0x.js/packages/sol-cov/README.md", "dependencies": { - "@0x/dev-utils": "^1.0.15", - "@0x/sol-compiler": "^1.1.10", - "@0x/subproviders": "^2.1.2", + "@0x/dev-utils": "^1.0.17", + "@0x/sol-compiler": "^1.1.12", + "@0x/subproviders": "^2.1.4", "@0x/typescript-typings": "^3.0.4", - "@0x/utils": "^2.0.4", - "@0x/web3-wrapper": "^3.1.2", + "@0x/utils": "^2.0.5", + "@0x/web3-wrapper": "^3.1.4", "@types/solidity-parser-antlr": "^0.2.0", "ethereum-types": "^1.1.2", "ethereumjs-util": "^5.1.1", diff --git a/packages/sol-doc/CHANGELOG.json b/packages/sol-doc/CHANGELOG.json index a2e903a24..a9547c883 100644 --- a/packages/sol-doc/CHANGELOG.json +++ b/packages/sol-doc/CHANGELOG.json @@ -1,5 +1,23 @@ [ { + "timestamp": 1542208198, + "version": "1.0.7", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { + "timestamp": 1542134075, + "version": "1.0.6", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "timestamp": 1542028948, "version": "1.0.5", "changes": [ diff --git a/packages/sol-doc/CHANGELOG.md b/packages/sol-doc/CHANGELOG.md index 257744b98..74a736c75 100644 --- a/packages/sol-doc/CHANGELOG.md +++ b/packages/sol-doc/CHANGELOG.md @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.7 - _November 14, 2018_ + + * Dependencies updated + +## v1.0.6 - _November 13, 2018_ + + * Dependencies updated + ## v1.0.5 - _November 12, 2018_ * Dependencies updated diff --git a/packages/sol-doc/package.json b/packages/sol-doc/package.json index 102911acc..e56408f98 100644 --- a/packages/sol-doc/package.json +++ b/packages/sol-doc/package.json @@ -1,6 +1,6 @@ { "name": "@0x/sol-doc", - "version": "1.0.5", + "version": "1.0.7", "description": "Solidity documentation generator", "main": "lib/src/index.js", "types": "lib/src/index.d.js", @@ -25,9 +25,9 @@ "author": "F. Eugene Aumson", "license": "Apache-2.0", "dependencies": { - "@0x/sol-compiler": "^1.1.10", + "@0x/sol-compiler": "^1.1.12", "@0x/types": "^1.2.1", - "@0x/utils": "^2.0.4", + "@0x/utils": "^2.0.5", "ethereum-types": "^1.1.2", "ethereumjs-util": "^5.1.1", "lodash": "^4.17.10", diff --git a/packages/sra-spec/CHANGELOG.json b/packages/sra-spec/CHANGELOG.json index 37ad85385..3cc917e5a 100644 --- a/packages/sra-spec/CHANGELOG.json +++ b/packages/sra-spec/CHANGELOG.json @@ -1,5 +1,14 @@ [ { + "timestamp": 1542208198, + "version": "1.0.10", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "timestamp": 1542028948, "version": "1.0.9", "changes": [ diff --git a/packages/sra-spec/CHANGELOG.md b/packages/sra-spec/CHANGELOG.md index c53ad6b5b..8b2b7747b 100644 --- a/packages/sra-spec/CHANGELOG.md +++ b/packages/sra-spec/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.10 - _November 14, 2018_ + + * Dependencies updated + ## v1.0.9 - _November 12, 2018_ * Dependencies updated diff --git a/packages/sra-spec/package.json b/packages/sra-spec/package.json index 1b7d0ccab..fac5a5a4b 100644 --- a/packages/sra-spec/package.json +++ b/packages/sra-spec/package.json @@ -1,6 +1,6 @@ { "name": "@0x/sra-spec", - "version": "1.0.9", + "version": "1.0.10", "engines": { "node": ">=6.12" }, @@ -35,7 +35,7 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/sra-spec/README.md", "dependencies": { - "@0x/json-schemas": "^2.1.0", + "@0x/json-schemas": "^2.1.1", "@loopback/openapi-v3-types": "^0.8.2" }, "devDependencies": { diff --git a/packages/subproviders/CHANGELOG.json b/packages/subproviders/CHANGELOG.json index 49b1d99d0..cdc150c3a 100644 --- a/packages/subproviders/CHANGELOG.json +++ b/packages/subproviders/CHANGELOG.json @@ -1,5 +1,23 @@ [ { + "timestamp": 1542208198, + "version": "2.1.4", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { + "timestamp": 1542134075, + "version": "2.1.3", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "timestamp": 1542028948, "version": "2.1.2", "changes": [ diff --git a/packages/subproviders/CHANGELOG.md b/packages/subproviders/CHANGELOG.md index a8ef8deb6..eda76bd2f 100644 --- a/packages/subproviders/CHANGELOG.md +++ b/packages/subproviders/CHANGELOG.md @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.1.4 - _November 14, 2018_ + + * Dependencies updated + +## v2.1.3 - _November 13, 2018_ + + * Dependencies updated + ## v2.1.2 - _November 12, 2018_ * Dependencies updated diff --git a/packages/subproviders/package.json b/packages/subproviders/package.json index c5a1dc3d1..d249e4d6d 100644 --- a/packages/subproviders/package.json +++ b/packages/subproviders/package.json @@ -1,6 +1,6 @@ { "name": "@0x/subproviders", - "version": "2.1.2", + "version": "2.1.4", "engines": { "node": ">=6.12" }, @@ -29,11 +29,11 @@ } }, "dependencies": { - "@0x/assert": "^1.0.16", + "@0x/assert": "^1.0.17", "@0x/types": "^1.2.1", "@0x/typescript-typings": "^3.0.4", - "@0x/utils": "^2.0.4", - "@0x/web3-wrapper": "^3.1.2", + "@0x/utils": "^2.0.5", + "@0x/web3-wrapper": "^3.1.4", "@ledgerhq/hw-app-eth": "^4.3.0", "@ledgerhq/hw-transport-u2f": "4.24.0", "@types/eth-lightwallet": "^3.0.0", diff --git a/packages/subproviders/src/subproviders/ledger.ts b/packages/subproviders/src/subproviders/ledger.ts index 28e348f90..b5ca10ce1 100644 --- a/packages/subproviders/src/subproviders/ledger.ts +++ b/packages/subproviders/src/subproviders/ledger.ts @@ -32,7 +32,6 @@ const DEFAULT_ADDRESS_SEARCH_LIMIT = 1000; */ export class LedgerSubprovider extends BaseWalletSubprovider { // tslint:disable-next-line:no-unused-variable - private readonly _nonceLock = new Lock(); private readonly _connectionLock = new Lock(); private readonly _networkId: number; private _baseDerivationPath: string; diff --git a/packages/subproviders/src/utils/wallet_utils.ts b/packages/subproviders/src/utils/wallet_utils.ts index 4c233645b..7027ca8a0 100644 --- a/packages/subproviders/src/utils/wallet_utils.ts +++ b/packages/subproviders/src/utils/wallet_utils.ts @@ -56,10 +56,11 @@ export const walletUtils = { parentDerivedKeyInfo: DerivedHDKeyInfo, searchLimit: number, ): DerivedHDKeyInfo | undefined { + const lowercaseAddress = address.toLowerCase(); let matchedKey: DerivedHDKeyInfo | undefined; const derivedKeyIterator = new DerivedHDKeyInfoIterator(parentDerivedKeyInfo, searchLimit); for (const key of derivedKeyIterator) { - if (key.address === address) { + if (key.address === lowercaseAddress) { matchedKey = key; break; } diff --git a/packages/testnet-faucets/package.json b/packages/testnet-faucets/package.json index fec328ec7..ac720a748 100644 --- a/packages/testnet-faucets/package.json +++ b/packages/testnet-faucets/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@0x/testnet-faucets", - "version": "1.0.54", + "version": "1.0.56", "engines": { "node": ">=6.12" }, @@ -18,11 +18,11 @@ "author": "Fabio Berger", "license": "Apache-2.0", "dependencies": { - "0x.js": "^2.0.2", - "@0x/subproviders": "^2.1.2", + "0x.js": "^2.0.4", + "@0x/subproviders": "^2.1.4", "@0x/typescript-typings": "^3.0.4", - "@0x/utils": "^2.0.4", - "@0x/web3-wrapper": "^3.1.2", + "@0x/utils": "^2.0.5", + "@0x/web3-wrapper": "^3.1.4", "body-parser": "^1.17.1", "ethereum-types": "^1.1.2", "ethereumjs-tx": "^1.3.5", diff --git a/packages/utils/CHANGELOG.json b/packages/utils/CHANGELOG.json index 6c9da5f37..1ef16e112 100644 --- a/packages/utils/CHANGELOG.json +++ b/packages/utils/CHANGELOG.json @@ -1,5 +1,14 @@ [ { + "timestamp": 1542208198, + "version": "2.0.5", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "version": "2.0.4", "changes": [ { diff --git a/packages/utils/CHANGELOG.md b/packages/utils/CHANGELOG.md index 4fdd13d9c..9846dd344 100644 --- a/packages/utils/CHANGELOG.md +++ b/packages/utils/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.0.5 - _November 14, 2018_ + + * Dependencies updated + ## v2.0.4 - _November 9, 2018_ * Dependencies updated diff --git a/packages/utils/package.json b/packages/utils/package.json index 24c2496b0..4b924226f 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@0x/utils", - "version": "2.0.4", + "version": "2.0.5", "engines": { "node": ">=6.12" }, diff --git a/packages/utils/test/sign_typed_data_utils_test.ts b/packages/utils/test/sign_typed_data_utils_test.ts index dcba08b04..3d2cb2496 100644 --- a/packages/utils/test/sign_typed_data_utils_test.ts +++ b/packages/utils/test/sign_typed_data_utils_test.ts @@ -136,5 +136,28 @@ describe('signTypedDataUtils', () => { const hashHex = `0x${hash}`; expect(hashHex).to.be.eq(orderSignTypedDataHashHex); }); + it('creates a hash of an uninitialized order', () => { + const uninitializedOrder = { + ...orderSignTypedData, + message: { + makerAddress: '0x0000000000000000000000000000000000000000', + takerAddress: '0x0000000000000000000000000000000000000000', + makerAssetAmount: 0, + takerAssetAmount: 0, + expirationTimeSeconds: 0, + makerFee: 0, + takerFee: 0, + feeRecipientAddress: '0x0000000000000000000000000000000000000000', + senderAddress: '0x0000000000000000000000000000000000000000', + salt: 0, + makerAssetData: '0x0000000000000000000000000000000000000000', + takerAssetData: '0x0000000000000000000000000000000000000000', + exchangeAddress: '0x0000000000000000000000000000000000000000', + }, + }; + const hash = signTypedDataUtils.generateTypedDataHash(uninitializedOrder).toString('hex'); + const hashHex = `0x${hash}`; + expect(hashHex).to.be.eq('0xfaa49b35faeb9197e9c3ba7a52075e6dad19739549f153b77dfcf59408a4b422'); + }); }); }); diff --git a/packages/web3-wrapper/CHANGELOG.json b/packages/web3-wrapper/CHANGELOG.json index 89d153eef..b938f6986 100644 --- a/packages/web3-wrapper/CHANGELOG.json +++ b/packages/web3-wrapper/CHANGELOG.json @@ -1,5 +1,23 @@ [ { + "timestamp": 1542208198, + "version": "3.1.4", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { + "timestamp": 1542134075, + "version": "3.1.3", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "timestamp": 1542028948, "version": "3.1.2", "changes": [ diff --git a/packages/web3-wrapper/CHANGELOG.md b/packages/web3-wrapper/CHANGELOG.md index 80ac76b9a..70a00ee1e 100644 --- a/packages/web3-wrapper/CHANGELOG.md +++ b/packages/web3-wrapper/CHANGELOG.md @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v3.1.4 - _November 14, 2018_ + + * Dependencies updated + +## v3.1.3 - _November 13, 2018_ + + * Dependencies updated + ## v3.1.2 - _November 12, 2018_ * Dependencies updated diff --git a/packages/web3-wrapper/package.json b/packages/web3-wrapper/package.json index 9f3bfe3c3..2469f7627 100644 --- a/packages/web3-wrapper/package.json +++ b/packages/web3-wrapper/package.json @@ -1,6 +1,6 @@ { "name": "@0x/web3-wrapper", - "version": "3.1.2", + "version": "3.1.4", "engines": { "node": ">=6.12" }, @@ -53,10 +53,10 @@ "typescript": "3.0.1" }, "dependencies": { - "@0x/assert": "^1.0.16", - "@0x/json-schemas": "^2.1.0", + "@0x/assert": "^1.0.17", + "@0x/json-schemas": "^2.1.1", "@0x/typescript-typings": "^3.0.4", - "@0x/utils": "^2.0.4", + "@0x/utils": "^2.0.5", "ethereum-types": "^1.1.2", "ethereumjs-util": "^5.1.1", "ethers": "~4.0.4", diff --git a/packages/website/package.json b/packages/website/package.json index 143751656..bb64b24db 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -1,6 +1,6 @@ { "name": "@0x/website", - "version": "0.0.57", + "version": "0.0.59", "engines": { "node": ">=6.12" }, @@ -20,16 +20,16 @@ "author": "Fabio Berger", "license": "Apache-2.0", "dependencies": { - "@0x/contract-wrappers": "^4.0.0", - "@0x/json-schemas": "^2.1.0", - "@0x/order-utils": "^3.0.0", - "@0x/react-docs": "^1.0.16", - "@0x/react-shared": "^1.0.19", - "@0x/subproviders": "^2.1.2", + "@0x/contract-wrappers": "^4.0.2", + "@0x/json-schemas": "^2.1.1", + "@0x/order-utils": "^3.0.2", + "@0x/react-docs": "^1.0.18", + "@0x/react-shared": "^1.0.21", + "@0x/subproviders": "^2.1.4", "@0x/types": "^1.2.1", "@0x/typescript-typings": "^3.0.4", - "@0x/utils": "^2.0.4", - "@0x/web3-wrapper": "^3.1.2", + "@0x/utils": "^2.0.5", + "@0x/web3-wrapper": "^3.1.4", "accounting": "^0.4.1", "basscss": "^8.0.3", "blockies": "^0.0.2", diff --git a/packages/website/public/images/developers/logo/0x.svg b/packages/website/public/images/developers/logo/0x.svg new file mode 100644 index 000000000..f02903925 --- /dev/null +++ b/packages/website/public/images/developers/logo/0x.svg @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 263.9 130.3" style="enable-background:new 0 0 263.9 130.3;" xml:space="preserve"> +<title>Asset 1</title> +<g> + <g id="Layer_1-2"> + <path d="M208.4,72.9c0,3.6-0.4,7.2-1.4,10.7c-0.8,2.8-2.2,5.5-4,7.8c-1.7,2.1-3.9,3.7-6.4,4.7c-5.5,2.1-11.6,2.1-17.1,0 + c-2.5-1.1-4.7-2.7-6.5-4.7c-1.9-2.3-3.3-5-4.1-7.8c-1-3.5-1.5-7.1-1.4-10.7v-15c0-3.6,0.4-7.2,1.4-10.7c0.8-2.8,2.2-5.5,4.1-7.8 + c1.7-2.1,3.9-3.7,6.4-4.7c2.7-1.1,5.6-1.7,8.5-1.6c2.9-0.1,5.9,0.5,8.6,1.6c2.5,1.1,4.7,2.7,6.5,4.7c1.9,2.3,3.3,5,4.1,7.8 + c1,3.5,1.5,7.1,1.4,10.7v15H208.4z M177.8,69l20.1-15c-0.2-4.3-1.2-7.5-2.8-9.5c-1.8-2.1-4.4-3.3-7.2-3.1c-3.4,0-5.9,1.2-7.6,3.6 + s-2.5,6.1-2.5,11V69z M198,62.1l-20.1,15c0.5,8.2,3.9,12.4,10.1,12.4c6.7,0,10.1-4.9,10.1-14.7V62.1L198,62.1z"/> + <path d="M240.7,63.9l11.2-15.8h11.5l-16.5,23l17,23.7h-11.5l-11.6-16.4l-11.5,16.4h-11.5l17-23.7l-16.5-23h11.5L240.7,63.9z"/> + <path d="M32.4,121.4c21,12.3,47.2,11.7,67.7-1.4c5.6-3.6,10.6-8,14.9-13.1c-3.5-4.9-7.3-10-11.3-15.2l-3.3-4.2 + C96.2,94.3,90,99.7,82.7,103L71.6,92.1L32.4,121.4z"/> + <path d="M8.6,32.7C3.7,41.4,0.8,51,0.1,60.9c-0.9,13.8,2.6,27.5,10.1,39.2c3.6,5.6,8,10.6,13.1,14.9c4.9-3.5,10-7.3,15.2-11.3 + l4.2-3.3C36,96.1,30.6,89.9,27.2,82.7l11-11.2L8.6,32.7z"/> + <path d="M97.8,8.8c-21-12.3-47.2-11.7-67.7,1.4c-5.6,3.6-10.6,8-14.9,13.1c3.5,4.9,7.3,10,11.3,15.2l3.3,4.2 + c4.3-6.7,10.5-12.1,17.7-15.5l0,0L58,37.4L97.8,8.8z"/> + <path d="M121.7,97.2c12-20.9,11.3-46.8-1.7-67.1c-3.6-5.6-8-10.6-13.1-14.9c-4.9,3.5-10,7.3-15.2,11.3l-4.2,3.3 + c6.8,4.3,12.2,10.4,15.6,17.7l0.1,0.2l-10.6,11L121.7,97.2z"/> + </g> +</g> +</svg> diff --git a/packages/website/public/images/developers/logo/docs.svg b/packages/website/public/images/developers/logo/docs.svg new file mode 100644 index 000000000..d3d14f66e --- /dev/null +++ b/packages/website/public/images/developers/logo/docs.svg @@ -0,0 +1,6 @@ +<svg width="46" height="18" viewBox="0 0 46 18" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M0.0195312 11.0918C0.0195312 9.30859 0.442057 7.8763 1.28711 6.79492C2.13216 5.70638 3.23861 5.16211 4.60645 5.16211C5.96712 5.16211 7.04492 5.6276 7.83984 6.55859V0.5H9.82715V17H8.00098L7.9043 15.7539C7.10938 16.7279 6.00293 17.2148 4.58496 17.2148C3.23861 17.2148 2.13932 16.6634 1.28711 15.5605C0.442057 14.4577 0.0195312 13.0182 0.0195312 11.2422V11.0918ZM2.00684 11.3174C2.00684 12.6351 2.27897 13.6663 2.82324 14.4111C3.36751 15.1559 4.11947 15.5283 5.0791 15.5283C6.33952 15.5283 7.25977 14.9626 7.83984 13.8311V8.49219C7.24544 7.39648 6.33236 6.84863 5.10059 6.84863C4.12663 6.84863 3.36751 7.22461 2.82324 7.97656C2.27897 8.72852 2.00684 9.84212 2.00684 11.3174Z" fill="#888888"/> +<path d="M12.3945 11.0811C12.3945 9.94238 12.6165 8.91829 13.0605 8.00879C13.5117 7.09928 14.1348 6.39746 14.9297 5.90332C15.7318 5.40918 16.6449 5.16211 17.6689 5.16211C19.2516 5.16211 20.5299 5.70996 21.5039 6.80566C22.485 7.90137 22.9756 9.35872 22.9756 11.1777V11.3174C22.9756 12.4489 22.7572 13.4658 22.3203 14.3682C21.8906 15.2633 21.2712 15.9616 20.4619 16.4629C19.6598 16.9642 18.736 17.2148 17.6904 17.2148C16.1149 17.2148 14.8366 16.667 13.8555 15.5713C12.8815 14.4756 12.3945 13.0254 12.3945 11.2207V11.0811ZM14.3926 11.3174C14.3926 12.6064 14.6898 13.6413 15.2842 14.4219C15.8857 15.2025 16.6878 15.5928 17.6904 15.5928C18.7002 15.5928 19.5023 15.1989 20.0967 14.4111C20.6911 13.6162 20.9883 12.5062 20.9883 11.0811C20.9883 9.80632 20.6839 8.77507 20.0752 7.9873C19.4736 7.19238 18.6715 6.79492 17.6689 6.79492C16.6878 6.79492 15.8965 7.18522 15.2949 7.96582C14.6934 8.74642 14.3926 9.86361 14.3926 11.3174Z" fill="#888888"/> +<path d="M30.1299 15.5928C30.8389 15.5928 31.4583 15.3779 31.9883 14.9482C32.5182 14.5186 32.8118 13.9814 32.8691 13.3369H34.749C34.7132 14.0029 34.484 14.6367 34.0615 15.2383C33.639 15.8398 33.0732 16.3197 32.3643 16.6777C31.6624 17.0358 30.9176 17.2148 30.1299 17.2148C28.5472 17.2148 27.2868 16.6885 26.3486 15.6357C25.4176 14.5758 24.9521 13.1292 24.9521 11.2959V10.9629C24.9521 9.83138 25.1598 8.8252 25.5752 7.94434C25.9906 7.06348 26.585 6.37956 27.3584 5.89258C28.139 5.4056 29.0592 5.16211 30.1191 5.16211C31.4225 5.16211 32.5039 5.55241 33.3633 6.33301C34.2298 7.11361 34.6917 8.12695 34.749 9.37305H32.8691C32.8118 8.62109 32.5254 8.00521 32.0098 7.52539C31.5013 7.03841 30.8711 6.79492 30.1191 6.79492C29.1094 6.79492 28.3252 7.16016 27.7666 7.89062C27.2152 8.61393 26.9395 9.66309 26.9395 11.0381V11.4141C26.9395 12.7533 27.2152 13.7845 27.7666 14.5078C28.318 15.2311 29.1058 15.5928 30.1299 15.5928Z" fill="#888888"/> +<path d="M43.751 13.917C43.751 13.3799 43.5469 12.9645 43.1387 12.6709C42.7376 12.3701 42.0322 12.1123 41.0225 11.8975C40.0199 11.6826 39.2214 11.4248 38.627 11.124C38.0397 10.8232 37.6029 10.4652 37.3164 10.0498C37.0371 9.63444 36.8975 9.1403 36.8975 8.56738C36.8975 7.61491 37.2985 6.80924 38.1006 6.15039C38.9098 5.49154 39.9411 5.16211 41.1943 5.16211C42.512 5.16211 43.5791 5.50228 44.3955 6.18262C45.2191 6.86296 45.6309 7.73307 45.6309 8.79297H43.6328C43.6328 8.2487 43.4001 7.77962 42.9346 7.38574C42.4762 6.99186 41.8962 6.79492 41.1943 6.79492C40.471 6.79492 39.9053 6.95247 39.4971 7.26758C39.0889 7.58268 38.8848 7.99447 38.8848 8.50293C38.8848 8.98275 39.0745 9.3444 39.4541 9.58789C39.8337 9.83138 40.5176 10.0641 41.5059 10.2861C42.5013 10.5081 43.307 10.7731 43.9229 11.0811C44.5387 11.389 44.9935 11.7614 45.2871 12.1982C45.5879 12.6279 45.7383 13.1543 45.7383 13.7773C45.7383 14.8158 45.3229 15.6501 44.4922 16.2803C43.6615 16.9033 42.5837 17.2148 41.2588 17.2148C40.3278 17.2148 39.5042 17.0501 38.7881 16.7207C38.0719 16.3913 37.5098 15.9329 37.1016 15.3457C36.7005 14.7513 36.5 14.1104 36.5 13.4229H38.4873C38.5231 14.0889 38.7881 14.6188 39.2822 15.0127C39.7835 15.3994 40.4424 15.5928 41.2588 15.5928C42.0107 15.5928 42.6123 15.4424 43.0635 15.1416C43.5218 14.8337 43.751 14.4255 43.751 13.917Z" fill="#888888"/> +</svg> diff --git a/packages/website/public/images/docs_logo.svg b/packages/website/public/images/docs_logo.svg deleted file mode 100644 index e3c4b628b..000000000 --- a/packages/website/public/images/docs_logo.svg +++ /dev/null @@ -1,24 +0,0 @@ -<svg width="278" height="72" viewBox="0 0 278 72" fill="none" xmlns="http://www.w3.org/2000/svg"> -<rect width="139" height="35.9999" fill="black" fill-opacity="0" transform="scale(2)"/> -<rect width="74.0872" height="35.9999" fill="black" fill-opacity="0" transform="scale(2)"/> -<path fill-rule="evenodd" clip-rule="evenodd" d="M115.234 33.6875L105.219 40.5522C105.477 44.3244 107.146 46.2101 110.226 46.2101C113.565 46.2101 115.234 43.9618 115.234 39.4642V33.6875ZM105.176 36.8338L115.191 29.9691C115.076 28.0041 114.607 26.5499 113.783 25.6069C112.959 24.6642 111.759 24.1924 110.183 24.1924C108.507 24.1924 107.253 24.7398 106.422 25.8345C105.591 26.9293 105.176 28.6109 105.176 30.8789V36.8338ZM120.394 38.6336C120.394 40.4934 120.16 42.1322 119.695 43.5498C119.229 44.9677 118.559 46.1546 117.686 47.1106C116.812 48.067 115.744 48.7892 114.483 49.277C113.222 49.7647 111.804 50.0089 110.228 50.0089C108.666 50.0089 107.255 49.7647 105.994 49.277C104.733 48.7892 103.662 48.067 102.781 47.1106C101.9 46.1546 101.22 44.9677 100.74 43.5498C100.259 42.1322 100.02 40.4934 100.02 38.6336V31.7491C100.02 29.8895 100.256 28.2511 100.729 26.8332C101.202 25.4156 101.879 24.2285 102.76 23.2721C103.641 22.3161 104.708 21.5941 105.962 21.1059C107.215 20.6182 108.623 20.374 110.185 20.374C111.761 20.374 113.179 20.6182 114.44 21.1059C115.701 21.5941 116.772 22.3161 117.653 23.2721C118.535 24.2285 119.212 25.4156 119.684 26.8332C120.157 28.2511 120.394 29.8895 120.394 31.7491V38.6336Z" fill="black"/> -<path fill-rule="evenodd" clip-rule="evenodd" d="M137.01 35.3006L142.385 27.7822H147.925L139.968 38.7311L148.175 50.0084H142.655L137.073 42.2232L131.511 50.0084H125.949L134.156 38.7311L126.199 27.7822H131.719L137.01 35.3006Z" fill="black"/> -<rect width="36" height="35.9999" fill="black" fill-opacity="0" transform="scale(2)"/> -<rect width="22.5701" height="11.8348" fill="black" fill-opacity="0" transform="translate(18.0508 48.3301) scale(2)"/> -<mask id="mask0" mask-type="alpha" maskUnits="userSpaceOnUse" x="18" y="48" width="46" height="24"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M18.0508 71.9997H63.191V48.3301H18.0508V71.9997Z" fill="white"/> -</mask> -<g mask="url(#mask0)"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M18.0508 67.1184C22.6601 69.8386 27.9385 71.5522 33.6161 71.9239C41.4577 72.4369 48.8759 70.3221 55.038 66.3361C58.1021 64.354 60.8534 61.9089 63.191 59.0905C61.2524 56.3622 59.1896 53.547 57.0111 50.6788L55.2311 48.3301C52.8884 52.0707 49.5201 55.0686 45.5555 56.9441L39.4742 50.9207L18.0508 67.1184Z" fill="black"/> -</g> -<rect width="11.6916" height="22.7346" fill="black" fill-opacity="0" transform="translate(0 18.6299) scale(2)"/> -<mask id="mask1" mask-type="alpha" maskUnits="userSpaceOnUse" x="0" y="18" width="24" height="47"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M0 18.6299V64.099H23.3833V18.6299L0 18.6299Z" fill="white"/> -</mask> -<g mask="url(#mask1)"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M4.72039 18.6299C2.09283 23.2496 0.437543 28.5247 0.0749777 34.193C-0.432648 42.134 1.66198 49.6455 5.60982 55.8826C7.56303 58.9691 9.97065 61.7416 12.7445 64.099C15.4422 62.1375 18.2268 60.0505 21.0641 57.847L23.3833 56.0501C19.6922 53.6805 16.7332 50.2769 14.8817 46.271L20.8743 40.0586L4.72039 18.6299Z" fill="black"/> -</g> -<path fill-rule="evenodd" clip-rule="evenodd" d="M54.0006 4.9803C49.4018 2.20541 44.1346 0.455949 38.4679 0.0772542C30.6434 -0.445638 23.2417 1.71125 17.096 5.77624C14.0546 7.78768 11.3231 10.2669 9 13.123C10.9328 15.9011 12.9892 18.7682 15.1602 21.69L16.9306 24.0783C19.2665 20.2759 22.622 17.2279 26.5714 15.3216L26.5759 15.3196L32.2882 21.07L54.0006 4.9803Z" fill="black"/> -<path fill-rule="evenodd" clip-rule="evenodd" d="M67.3574 53.7131C69.9376 49.2175 71.5681 44.0992 71.9269 38.5992C72.4342 30.8203 70.3437 23.4613 66.4027 17.3486C64.4432 14.3092 62.0259 11.58 59.2397 9.26074C56.5427 11.1839 53.7595 13.2301 50.9238 15.3915L48.6016 17.1569C52.3061 19.4848 55.2749 22.8335 57.1283 26.7755L57.1639 26.8575L51.3951 32.7948L67.3574 53.7131Z" fill="black"/> -<path d="M164.041 38.1836C164.041 34.6172 164.886 31.7526 166.576 29.5898C168.266 27.4128 170.479 26.3242 173.215 26.3242C175.936 26.3242 178.092 27.2552 179.682 29.1172V17H183.656V50H180.004L179.811 47.5078C178.221 49.4557 176.008 50.4297 173.172 50.4297C170.479 50.4297 168.281 49.3268 166.576 47.1211C164.886 44.9154 164.041 42.0365 164.041 38.4844V38.1836ZM168.016 38.6348C168.016 41.2702 168.56 43.3327 169.648 44.8223C170.737 46.3118 172.241 47.0566 174.16 47.0566C176.681 47.0566 178.521 45.9251 179.682 43.6621V32.9844C178.493 30.793 176.667 29.6973 174.203 29.6973C172.255 29.6973 170.737 30.4492 169.648 31.9531C168.56 33.457 168.016 35.6842 168.016 38.6348ZM188.791 38.1621C188.791 35.8848 189.235 33.8366 190.123 32.0176C191.025 30.1986 192.271 28.7949 193.861 27.8066C195.465 26.8184 197.292 26.3242 199.34 26.3242C202.505 26.3242 205.062 27.4199 207.01 29.6113C208.972 31.8027 209.953 34.7174 209.953 38.3555V38.6348C209.953 40.8978 209.516 42.9316 208.643 44.7363C207.783 46.5267 206.544 47.9232 204.926 48.9258C203.322 49.9284 201.474 50.4297 199.383 50.4297C196.232 50.4297 193.675 49.334 191.713 47.1426C189.765 44.9512 188.791 42.0508 188.791 38.4414V38.1621ZM192.787 38.6348C192.787 41.2129 193.382 43.2826 194.57 44.8438C195.773 46.4049 197.378 47.1855 199.383 47.1855C201.402 47.1855 203.007 46.3978 204.195 44.8223C205.384 43.2324 205.979 41.0124 205.979 38.1621C205.979 35.6126 205.37 33.5501 204.152 31.9746C202.949 30.3848 201.345 29.5898 199.34 29.5898C197.378 29.5898 195.795 30.3704 194.592 31.9316C193.389 33.4928 192.787 35.7272 192.787 38.6348ZM224.262 47.1855C225.68 47.1855 226.919 46.7559 227.979 45.8965C229.038 45.0371 229.626 43.9629 229.74 42.6738H233.5C233.428 44.0059 232.97 45.2734 232.125 46.4766C231.28 47.6797 230.148 48.6393 228.73 49.3555C227.327 50.0716 225.837 50.4297 224.262 50.4297C221.096 50.4297 218.576 49.377 216.699 47.2715C214.837 45.1517 213.906 42.2585 213.906 38.5918V37.9258C213.906 35.6628 214.322 33.6504 215.152 31.8887C215.983 30.127 217.172 28.7591 218.719 27.7852C220.28 26.8112 222.12 26.3242 224.24 26.3242C226.847 26.3242 229.01 27.1048 230.729 28.666C232.462 30.2272 233.385 32.2539 233.5 34.7461H229.74C229.626 33.2422 229.053 32.0104 228.021 31.0508C227.005 30.0768 225.744 29.5898 224.24 29.5898C222.221 29.5898 220.652 30.3203 219.535 31.7812C218.432 33.2279 217.881 35.3262 217.881 38.0762V38.8281C217.881 41.5065 218.432 43.569 219.535 45.0156C220.638 46.4622 222.214 47.1855 224.262 47.1855ZM251.504 43.834C251.504 42.7598 251.096 41.929 250.279 41.3418C249.477 40.7402 248.066 40.2246 246.047 39.7949C244.042 39.3652 242.445 38.8496 241.256 38.248C240.081 37.6465 239.208 36.9303 238.635 36.0996C238.076 35.2689 237.797 34.2806 237.797 33.1348C237.797 31.2298 238.599 29.6185 240.203 28.3008C241.822 26.9831 243.884 26.3242 246.391 26.3242C249.026 26.3242 251.16 27.0046 252.793 28.3652C254.44 29.7259 255.264 31.4661 255.264 33.5859H251.268C251.268 32.4974 250.802 31.5592 249.871 30.7715C248.954 29.9837 247.794 29.5898 246.391 29.5898C244.944 29.5898 243.812 29.9049 242.996 30.5352C242.18 31.1654 241.771 31.9889 241.771 33.0059C241.771 33.9655 242.151 34.6888 242.91 35.1758C243.669 35.6628 245.037 36.1283 247.014 36.5723C249.005 37.0163 250.616 37.5462 251.848 38.1621C253.079 38.778 253.989 39.5228 254.576 40.3965C255.178 41.2559 255.479 42.3086 255.479 43.5547C255.479 45.6315 254.648 47.3001 252.986 48.5605C251.325 49.8066 249.169 50.4297 246.52 50.4297C244.658 50.4297 243.01 50.1003 241.578 49.4414C240.146 48.7826 239.021 47.8659 238.205 46.6914C237.403 45.5026 237.002 44.2207 237.002 42.8457H240.977C241.048 44.1777 241.578 45.2376 242.566 46.0254C243.569 46.7988 244.887 47.1855 246.52 47.1855C248.023 47.1855 249.227 46.8848 250.129 46.2832C251.046 45.6673 251.504 44.8509 251.504 43.834Z" fill="#666666"/> -</svg> diff --git a/packages/website/translations/chinese.json b/packages/website/translations/chinese.json index 95b9bda62..e3798a072 100644 --- a/packages/website/translations/chinese.json +++ b/packages/website/translations/chinese.json @@ -83,7 +83,7 @@ "BUILD_A_RELAYER_DESCRIPTION": "Learn how to build your own 0x relayer from scratch", "DEVELOP_ON_ETHEREUM": "develop on Ethereum", "DEVELOP_ON_ETHEREUM_DESCRIPTION": "Learn more about building applications ontop of Ethereum", - "ORDER_BASICS": "0x order basics", + "ORDER_BASICS": "Make & take orders", "ORDER_BASICS_DESCRIPTION": "Tutorial on how to create, validate and fill an order using 0x", "USE_NETWORKED_LIQUIDITY": "use networked liquidity", "USE_NETWORKED_LIQUIDITY_DESCRIPTION": "Access the shared liquidity pool using the Standard Relayer API", diff --git a/packages/website/translations/english.json b/packages/website/translations/english.json index 465bd49b4..f91633492 100644 --- a/packages/website/translations/english.json +++ b/packages/website/translations/english.json @@ -87,7 +87,7 @@ "BUILD_A_RELAYER_DESCRIPTION": "Learn how to build your own 0x relayer from scratch", "DEVELOP_ON_ETHEREUM": "develop on Ethereum", "DEVELOP_ON_ETHEREUM_DESCRIPTION": "Learn more about building applications ontop of Ethereum", - "ORDER_BASICS": "0x order basics", + "ORDER_BASICS": "Make & take orders", "ORDER_BASICS_DESCRIPTION": "Tutorial on how to create, validate and fill an order using 0x", "USE_NETWORKED_LIQUIDITY": "use networked liquidity", "USE_NETWORKED_LIQUIDITY_DESCRIPTION": "Learn how to tap into networked liquidity using the Standard Relayer API", diff --git a/packages/website/translations/korean.json b/packages/website/translations/korean.json index 5485e5583..ca8cbe3d0 100644 --- a/packages/website/translations/korean.json +++ b/packages/website/translations/korean.json @@ -83,7 +83,7 @@ "BUILD_A_RELAYER_DESCRIPTION": "Learn how to build your own 0x relayer from scratch", "DEVELOP_ON_ETHEREUM": "develop on Ethereum", "DEVELOP_ON_ETHEREUM_DESCRIPTION": "Learn more about building applications ontop of Ethereum", - "ORDER_BASICS": "0x order basics", + "ORDER_BASICS": "Make & take orders", "ORDER_BASICS_DESCRIPTION": "Tutorial on how to create, validate and fill an order using 0x", "USE_NETWORKED_LIQUIDITY": "use networked liquidity", "USE_NETWORKED_LIQUIDITY_DESCRIPTION": "Learn how to tap into networked liquidity using the Standard Relayer API", diff --git a/packages/website/translations/russian.json b/packages/website/translations/russian.json index a2e2a8cef..a43d1e7b7 100644 --- a/packages/website/translations/russian.json +++ b/packages/website/translations/russian.json @@ -83,7 +83,7 @@ "BUILD_A_RELAYER_DESCRIPTION": "Learn how to build your own 0x relayer from scratch", "DEVELOP_ON_ETHEREUM": "develop on Ethereum", "DEVELOP_ON_ETHEREUM_DESCRIPTION": "Learn more about building applications ontop of Ethereum", - "ORDER_BASICS": "0x order basics", + "ORDER_BASICS": "Make & take orders", "ORDER_BASICS_DESCRIPTION": "Tutorial on how to create, validate and fill an order using 0x", "USE_NETWORKED_LIQUIDITY": "use networked liquidity", "USE_NETWORKED_LIQUIDITY_DESCRIPTION": "Learn how to tap into networked liquidity using the Standard Relayer API", diff --git a/packages/website/translations/spanish.json b/packages/website/translations/spanish.json index acbc59c3d..4d404ac6b 100644 --- a/packages/website/translations/spanish.json +++ b/packages/website/translations/spanish.json @@ -84,7 +84,7 @@ "BUILD_A_RELAYER_DESCRIPTION": "Learn how to build your own 0x relayer from scratch", "DEVELOP_ON_ETHEREUM": "develop on Ethereum", "DEVELOP_ON_ETHEREUM_DESCRIPTION": "Learn more about building applications ontop of Ethereum", - "ORDER_BASICS": "0x order basics", + "ORDER_BASICS": "Make & take orders", "ORDER_BASICS_DESCRIPTION": "Tutorial on how to create, validate and fill an order using 0x", "USE_NETWORKED_LIQUIDITY": "use networked liquidity", "USE_NETWORKED_LIQUIDITY_DESCRIPTION": "Learn how to tap into networked liquidity using the Standard Relayer API", diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index 25430c1c7..37f746f7c 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -591,7 +591,7 @@ export class Blockchain { } } else { const injectedWeb3IfExists = (window as any).web3; - if (!_.isUndefined(injectedWeb3IfExists.currentProvider)) { + if (!_.isUndefined(injectedWeb3IfExists) && !_.isUndefined(injectedWeb3IfExists.currentProvider)) { injectedProviderIfExists = injectedWeb3IfExists.currentProvider; } else { return undefined; diff --git a/packages/website/ts/components/documentation/docs_logo.tsx b/packages/website/ts/components/documentation/docs_logo.tsx index 2840d5960..ac331db79 100644 --- a/packages/website/ts/components/documentation/docs_logo.tsx +++ b/packages/website/ts/components/documentation/docs_logo.tsx @@ -1,17 +1,34 @@ import { Link } from '@0x/react-shared'; import * as React from 'react'; +import { styled } from 'ts/style/theme'; import { WebsitePaths } from 'ts/types'; +import { Container } from '../ui/container'; + export interface DocsLogoProps { - height: number; containerStyle?: React.CSSProperties; } +const Image = styled.img` + &:hover { + opacity: 0.7; + } +`; + export const DocsLogo: React.StatelessComponent<DocsLogoProps> = props => { return ( - <Link to={WebsitePaths.Docs}> - <img src="/images/docs_logo.svg" height={props.height} /> - </Link> + <Container className="flex"> + <Container> + <Link to={WebsitePaths.Home}> + <Image src="/images/developers/logo/0x.svg" height={34} /> + </Link> + </Container> + <Container paddingTop="6px" paddingLeft="7px"> + <Link to={WebsitePaths.Docs}> + <Image src="/images/developers/logo/docs.svg" height={20} /> + </Link> + </Container> + </Container> ); }; diff --git a/packages/website/ts/components/documentation/docs_top_bar.tsx b/packages/website/ts/components/documentation/docs_top_bar.tsx index 2054d0860..c4291b78f 100644 --- a/packages/website/ts/components/documentation/docs_top_bar.tsx +++ b/packages/website/ts/components/documentation/docs_top_bar.tsx @@ -38,17 +38,17 @@ export class DocsTopBar extends React.Component<DocsTopBarProps, DocsTopBarState return ( <Container height={80}> <Container - className="flex items-center lg-pt3 md-pt3 sm-pt1 lg-mt1 md-mt1 sm-mt0 lg-justify-end md-justify-end sm-justify-start" + className="flex items-center lg-pt3 md-pt3 sm-pt1 lg-justify-end md-justify-end sm-justify-start" width="100%" > <Container className="sm-hide xs-hide"> - <Container className="flex items-center justify-between right" width="300px"> + <Container className="flex items-center justify-between right" width="250px"> {this._renderMenuItems(constants.DEVELOPER_TOPBAR_LINKS)} </Container> </Container> <Container className="lg-hide md-hide"> <Container paddingTop="6px"> - <DocsLogo height={30} /> + <DocsLogo /> </Container> </Container> <Container className="md-hide lg-hide absolute" right="18px" top="12px"> @@ -63,7 +63,7 @@ export class DocsTopBar extends React.Component<DocsTopBarProps, DocsTopBarState /> </Container> </Container> - <Container width={'100%'} height={'1px'} backgroundColor={colors.grey300} marginTop={'13px'} /> + <Container width={'100%'} height={'1px'} backgroundColor={colors.grey300} marginTop={'16px'} /> {this.props.screenWidth === ScreenWidths.Sm && this._renderDrawer()} </Container> ); diff --git a/packages/website/ts/components/documentation/sidebar_header.tsx b/packages/website/ts/components/documentation/sidebar_header.tsx index fece2704b..9ced52c74 100644 --- a/packages/website/ts/components/documentation/sidebar_header.tsx +++ b/packages/website/ts/components/documentation/sidebar_header.tsx @@ -29,6 +29,7 @@ export const SidebarHeader: React.StatelessComponent<SidebarHeaderProps> = ({ fontColor={colors.lightLinkBlue} fontSize={screenWidth === ScreenWidths.Sm ? '20px' : '22px'} fontWeight="bold" + lineHeight="26px" > {title} </Text> @@ -36,7 +37,7 @@ export const SidebarHeader: React.StatelessComponent<SidebarHeaderProps> = ({ {!_.isUndefined(docsVersion) && !_.isUndefined(availableDocVersions) && !_.isUndefined(onVersionSelected) && ( - <div className="right" style={{ alignSelf: 'flex-end' }}> + <div className="right" style={{ alignSelf: 'flex-end', paddingBottom: 4 }}> <VersionDropDown selectedVersion={docsVersion} versions={availableDocVersions} diff --git a/packages/website/ts/components/documentation/version_drop_down.tsx b/packages/website/ts/components/documentation/version_drop_down.tsx index a1c8b0547..5e77530fd 100644 --- a/packages/website/ts/components/documentation/version_drop_down.tsx +++ b/packages/website/ts/components/documentation/version_drop_down.tsx @@ -27,7 +27,7 @@ const PlainActiveNode: React.StatelessComponent<ActiveNodeProps> = ({ className, const ActiveNode = styled(PlainActiveNode)` cursor: pointer; - border: 2px solid ${colors.beigeWhite}; + border: 1px solid ${colors.beigeWhite}; border-radius: 4px; padding: 4px 6px 4px 8px; `; diff --git a/packages/website/ts/components/dropdowns/developers_drop_down.tsx b/packages/website/ts/components/dropdowns/developers_drop_down.tsx index 6e85c1499..b8a35fab0 100644 --- a/packages/website/ts/components/dropdowns/developers_drop_down.tsx +++ b/packages/website/ts/components/dropdowns/developers_drop_down.tsx @@ -89,7 +89,7 @@ export class DevelopersDropDown extends React.Component<DevelopersDropDownProps, ); } private _renderDropdownMenu(): React.ReactNode { - const sectionPadding = '28px'; + const sectionPadding = '26px'; const dropdownMenu = ( <Container> <Container className="flex" padding={sectionPadding}> diff --git a/packages/website/ts/components/nested_sidebar_menu.tsx b/packages/website/ts/components/nested_sidebar_menu.tsx index db7d55261..4d4bc4617 100644 --- a/packages/website/ts/components/nested_sidebar_menu.tsx +++ b/packages/website/ts/components/nested_sidebar_menu.tsx @@ -4,11 +4,13 @@ import * as _ from 'lodash'; import * as React from 'react'; import { Button } from 'ts/components/ui/button'; import { Text } from 'ts/components/ui/text'; +import { ScreenWidths } from 'ts/types'; export interface NestedSidebarMenuProps { sectionNameToLinks: ObjectMap<ALink[]>; sidebarHeader?: React.ReactNode; shouldReformatMenuItemNames?: boolean; + screenWidth: ScreenWidths; } export const NestedSidebarMenu = (props: NestedSidebarMenuProps) => { @@ -22,7 +24,7 @@ export const NestedSidebarMenu = (props: NestedSidebarMenuProps) => { ...link, title: menuItemTitle, }; - return <MenuItem key={`menu-item-${menuItemTitle}`} link={finalLink} />; + return <MenuItem key={`menu-item-${menuItemTitle}`} link={finalLink} screenWidth={props.screenWidth} />; }); // tslint:disable-next-line:no-unused-variable return ( @@ -44,6 +46,7 @@ export const NestedSidebarMenu = (props: NestedSidebarMenuProps) => { export interface MenuItemProps { link: ALink; + screenWidth: ScreenWidths; } export interface MenuItemState { @@ -70,7 +73,13 @@ export class MenuItem extends React.Component<MenuItemProps, MenuItemState> { borderRadius="4px" padding="0.4em 0.375em" width="100%" - backgroundColor={isActive ? colors.lightLinkBlue : colors.grey100} + backgroundColor={ + isActive + ? colors.lightLinkBlue + : this.props.screenWidth === ScreenWidths.Sm + ? 'white' + : colors.grey100 + } fontSize="14px" textAlign="left" > diff --git a/packages/website/ts/pages/documentation/developers_page.tsx b/packages/website/ts/pages/documentation/developers_page.tsx index 89389e488..a84be7bfe 100644 --- a/packages/website/ts/pages/documentation/developers_page.tsx +++ b/packages/website/ts/pages/documentation/developers_page.tsx @@ -157,9 +157,10 @@ export class DevelopersPage extends React.Component<DevelopersPageProps, Develop > <Container borderBottom={this.state.isSidebarScrolling ? `1px solid ${colors.grey300}` : 'none'} + paddingBottom="2px" > <Container paddingTop="30px" paddingLeft="10px" paddingBottom="8px"> - <DocsLogo height={36} /> + <DocsLogo /> </Container> </Container> <SidebarContainer onWheel={this._throttledSidebarScrolling}> diff --git a/packages/website/ts/pages/documentation/doc_page.tsx b/packages/website/ts/pages/documentation/doc_page.tsx index 8392c90e4..adf2261b5 100644 --- a/packages/website/ts/pages/documentation/doc_page.tsx +++ b/packages/website/ts/pages/documentation/doc_page.tsx @@ -94,7 +94,11 @@ export class DocPage extends React.Component<DocPageProps, DocPageState> { const sidebar = _.isUndefined(this.state.docAgnosticFormat) ? ( <div /> ) : ( - <NestedSidebarMenu sidebarHeader={this._renderSidebarHeader()} sectionNameToLinks={sectionNameToLinks} /> + <NestedSidebarMenu + sidebarHeader={this._renderSidebarHeader()} + sectionNameToLinks={sectionNameToLinks} + screenWidth={this.props.screenWidth} + /> ); return ( <DevelopersPage diff --git a/packages/website/ts/pages/documentation/docs_home.tsx b/packages/website/ts/pages/documentation/docs_home.tsx index bbbee2d2d..017573304 100644 --- a/packages/website/ts/pages/documentation/docs_home.tsx +++ b/packages/website/ts/pages/documentation/docs_home.tsx @@ -346,6 +346,7 @@ export class DocsHome extends React.Component<DocsHomeProps, DocsHomeState> { sidebarHeader={isSmallScreen ? this._renderSidebarHeader() : undefined} sectionNameToLinks={sectionNameToLinks} shouldReformatMenuItemNames={false} + screenWidth={this.props.screenWidth} /> ); return ( diff --git a/packages/website/ts/pages/wiki/wiki.tsx b/packages/website/ts/pages/wiki/wiki.tsx index c1802b1f8..c3c1600a5 100644 --- a/packages/website/ts/pages/wiki/wiki.tsx +++ b/packages/website/ts/pages/wiki/wiki.tsx @@ -76,6 +76,7 @@ export class Wiki extends React.Component<WikiProps, WikiState> { <NestedSidebarMenu sidebarHeader={isSmallScreen ? this._renderSidebarHeader() : undefined} sectionNameToLinks={sectionNameToLinks} + screenWidth={this.props.screenWidth} /> ); return ( diff --git a/packages/website/ts/utils/constants.ts b/packages/website/ts/utils/constants.ts index 0a8a86c50..379f28022 100644 --- a/packages/website/ts/utils/constants.ts +++ b/packages/website/ts/utils/constants.ts @@ -104,10 +104,6 @@ export const constants = { URL_MISSION_AND_VALUES_BLOG_POST: 'https://blog.0xproject.com/the-0x-mission-and-values-181a58706f9f', DEVELOPER_TOPBAR_LINKS: [ { - title: Key.Home, - to: WebsitePaths.Home, - }, - { title: Key.Wiki, to: WebsitePaths.Wiki, }, diff --git a/packages/website/webpack.config.js b/packages/website/webpack.config.js index ec265be93..33d5f648b 100644 --- a/packages/website/webpack.config.js +++ b/packages/website/webpack.config.js @@ -100,7 +100,6 @@ module.exports = (_env, argv) => { new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), new webpack.DefinePlugin({ 'process.env': { - NODE_ENV: JSON.stringify(process.env.NODE_ENV), GIT_SHA: JSON.stringify(GIT_SHA), }, }), diff --git a/python-packages/order_utils/.pylintrc b/python-packages/order_utils/.pylintrc new file mode 100644 index 000000000..937bc6313 --- /dev/null +++ b/python-packages/order_utils/.pylintrc @@ -0,0 +1,3 @@ +[MESSAGES CONTROL] +disable=C0330,line-too-long,fixme,too-few-public-methods,too-many-ancestors +# C0330 is "bad hanging indent". we use indents per `black`. diff --git a/python-packages/order_utils/setup.py b/python-packages/order_utils/setup.py index 1b07b612c..679bfb4b2 100755 --- a/python-packages/order_utils/setup.py +++ b/python-packages/order_utils/setup.py @@ -24,7 +24,6 @@ class TestCommandExtension(TestCommand): exit(pytest.main()) -# pylint: disable=too-many-ancestors class LintCommand(distutils.command.build_py.build_py): """Custom setuptools command class for running linters.""" @@ -90,7 +89,6 @@ class CleanCommandExtension(clean): rmtree("src/0x_order_utils.egg-info", ignore_errors=True) -# pylint: disable=too-many-ancestors class TestPublishCommand(distutils.command.build_py.build_py): """Custom command to publish to test.pypi.org.""" @@ -108,7 +106,6 @@ class TestPublishCommand(distutils.command.build_py.build_py): ) -# pylint: disable=too-many-ancestors class PublishCommand(distutils.command.build_py.build_py): """Custom command to publish to pypi.org.""" @@ -119,7 +116,6 @@ class PublishCommand(distutils.command.build_py.build_py): subprocess.check_call("twine upload dist/*".split()) # nosec -# pylint: disable=too-many-ancestors class GanacheCommand(distutils.command.build_py.build_py): """Custom command to publish to pypi.org.""" @@ -160,7 +156,13 @@ setup( "publish": PublishCommand, "ganache": GanacheCommand, }, - install_requires=["eth-abi", "eth_utils", "mypy_extensions", "web3"], + install_requires=[ + "eth-abi", + "eth_utils", + "jsonschema", + "mypy_extensions", + "web3", + ], extras_require={ "dev": [ "bandit", @@ -182,6 +184,7 @@ setup( package_data={ "zero_ex.order_utils": ["py.typed"], "zero_ex.contract_artifacts": ["artifacts/*"], + "zero_ex.json_schemas": ["schemas/*"], }, package_dir={"": "src"}, license="Apache 2.0", diff --git a/python-packages/order_utils/src/index.rst b/python-packages/order_utils/src/index.rst index b99addabd..551487ab1 100644 --- a/python-packages/order_utils/src/index.rst +++ b/python-packages/order_utils/src/index.rst @@ -7,6 +7,11 @@ Python zero_ex.order_utils :maxdepth: 2 :caption: Contents: +.. autoclass:: zero_ex.order_utils.Order + :members: + +See source for class properties. Sphinx does not easily generate class property docs; pull requests welcome. + .. automodule:: zero_ex.order_utils :members: @@ -17,9 +22,7 @@ Python zero_ex.order_utils .. autoclass:: zero_ex.order_utils.asset_data_utils.ERC721AssetData -See source for class properties. Sphinx does not easily generate class property docs; pull requests welcome. - -.. automodule:: zero_ex.order_utils.signature_utils +.. automodule:: zero_ex.json_schemas :members: Indices and tables diff --git a/python-packages/order_utils/src/zero_ex/dev_utils/abi_utils.py b/python-packages/order_utils/src/zero_ex/dev_utils/abi_utils.py index 71b6128ca..3fec775b0 100644 --- a/python-packages/order_utils/src/zero_ex/dev_utils/abi_utils.py +++ b/python-packages/order_utils/src/zero_ex/dev_utils/abi_utils.py @@ -10,8 +10,8 @@ from typing import Any, List from mypy_extensions import TypedDict -from eth_abi import encode_abi from web3 import Web3 +from eth_abi import encode_abi from .type_assertions import assert_is_string, assert_is_list @@ -84,7 +84,6 @@ def method_id(name: str, types: List[str]) -> str: def simple_encode(method: str, *args: Any) -> bytes: - # docstring considered all one line by pylint: disable=line-too-long r"""Encode a method ABI. >>> simple_encode("ERC20Token(address)", "0x1dc4c1cefef38a777b15aa20260a54e584b16c48") diff --git a/python-packages/order_utils/src/zero_ex/dev_utils/type_assertions.py b/python-packages/order_utils/src/zero_ex/dev_utils/type_assertions.py index 08c1b0ea5..1dcfb39a9 100644 --- a/python-packages/order_utils/src/zero_ex/dev_utils/type_assertions.py +++ b/python-packages/order_utils/src/zero_ex/dev_utils/type_assertions.py @@ -2,6 +2,9 @@ from typing import Any +from eth_utils import is_address +from web3.providers.base import BaseProvider + def assert_is_string(value: Any, name: str) -> None: """If :param value: isn't of type str, raise a TypeError. @@ -56,3 +59,31 @@ def assert_is_hex_string(value: Any, name: str) -> None: """ assert_is_string(value, name) int(value, 16) # raises a ValueError if value isn't a base-16 str + + +def assert_is_address(value: Any, name: str) -> None: + """Assert that `value` is a valid Ethereum address. + + If `value` isn't a hex string, raise a TypeError. If it isn't a valid + Ethereum address, raise a ValueError. + """ + assert_is_hex_string(value, name) + if not is_address(value): + raise ValueError( + f"Expected variable '{name}' to be a valid Ethereum" + + " address, but it's not." + ) + + +def assert_is_provider(value: Any, name: str) -> None: + """Assert that `value` is a Web3 provider. + + If `value` isn't a Web3 provider, raise a TypeError. + """ + # TODO: make this provider check more flexible. + # https://app.asana.com/0/684263176955174/901300863045491/f + if not isinstance(value, BaseProvider): + raise TypeError( + f"Expected variable '{name}' to be an instance of a Web3 provider," + + " but it's not." + ) diff --git a/python-packages/order_utils/src/zero_ex/json_schemas/__init__.py b/python-packages/order_utils/src/zero_ex/json_schemas/__init__.py new file mode 100644 index 000000000..2a1728b8a --- /dev/null +++ b/python-packages/order_utils/src/zero_ex/json_schemas/__init__.py @@ -0,0 +1,61 @@ +"""JSON schemas and associated utilities.""" + +from os import path +import json +from typing import Mapping + +from pkg_resources import resource_string +import jsonschema + + +def assert_valid(data: Mapping, schema_id: str) -> None: + """Validate the given `data` against the specified `schema`. + + :param data: Python dictionary to be validated as a JSON object. + :param schema_id: id property of the JSON schema to validate against. Must + be one of those listed in `the 0x JSON schema files + <https://github.com/0xProject/0x-monorepo/tree/development/packages/json-schemas/schemas>`_. + + Raises an exception if validation fails. + + >>> assert_valid( + ... {'v': 27, 'r': '0x'+'f'*64, 's': '0x'+'f'*64}, + ... '/ECSignature', + ... ) + """ + # noqa + class LocalRefResolver(jsonschema.RefResolver): + """Resolve package-local JSON schema id's.""" + + def __init__(self): + self.ref_to_file = { + "/addressSchema": "address_schema.json", + "/hexSchema": "hex_schema.json", + "/orderSchema": "order_schema.json", + "/wholeNumberSchema": "whole_number_schema.json", + "/ECSignature": "ec_signature_schema.json", + "/ecSignatureParameterSchema": ( + "ec_signature_parameter_schema.json" + "" + ), + } + jsonschema.RefResolver.__init__(self, "", "") + + def resolve_from_url(self, url): + """Resolve the given URL.""" + ref = url.replace("file://", "") + if ref in self.ref_to_file: + return json.loads( + resource_string( + "zero_ex.json_schemas", + f"schemas/{self.ref_to_file[ref]}", + ) + ) + raise jsonschema.ValidationError( + f"Unknown ref '{ref}'. " + + f"Known refs: {list(self.ref_to_file.keys())}." + ) + + resolver = LocalRefResolver() + jsonschema.validate( + data, resolver.resolve_from_url(schema_id), resolver=resolver + ) diff --git a/python-packages/order_utils/src/zero_ex/json_schemas/schemas b/python-packages/order_utils/src/zero_ex/json_schemas/schemas new file mode 120000 index 000000000..b8257372c --- /dev/null +++ b/python-packages/order_utils/src/zero_ex/json_schemas/schemas @@ -0,0 +1 @@ +../../../../../packages/json-schemas/schemas/
\ No newline at end of file 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 index 80445cb6e..24c6bfd4e 100644 --- a/python-packages/order_utils/src/zero_ex/order_utils/__init__.py +++ b/python-packages/order_utils/src/zero_ex/order_utils/__init__.py @@ -9,3 +9,397 @@ just this purpose. To start it: ``docker run -d -p 8545:8545 0xorg/ganache-cli --networkId 50 -m "concert load couple harbor equip island argue ramp clarify fence smart topic"``. """ + +from enum import auto, Enum +import json +from typing import Dict, Tuple +from pkg_resources import resource_string + +from mypy_extensions import TypedDict + +from eth_utils import keccak, to_bytes, to_checksum_address +from web3 import Web3 +import web3.exceptions +from web3.providers.base import BaseProvider +from web3.utils import datatypes + +from zero_ex.dev_utils.type_assertions import ( + assert_is_address, + assert_is_hex_string, + assert_is_provider, +) +from zero_ex.json_schemas import assert_valid + + +class _Constants: + """Static data used by order utilities.""" + + _contract_name_to_abi: Dict[str, Dict] = {} # class data, not instance + + @classmethod + def contract_name_to_abi(cls, contract_name: str) -> Dict: + """Return the ABI for the given contract name. + + First tries to get data from the class level storage + `_contract_name_to_abi`. If it's not there, loads it from disk, stores + it in the class data (for the next caller), and then returns it. + """ + try: + return cls._contract_name_to_abi[contract_name] + except KeyError: + cls._contract_name_to_abi[contract_name] = json.loads( + resource_string( + "zero_ex.contract_artifacts", + f"artifacts/{contract_name}.json", + ) + )["compilerOutput"]["abi"] + return cls._contract_name_to_abi[contract_name] + + network_to_exchange_addr: Dict[str, str] = { + "1": "0x4f833a24e1f95d70f028921e27040ca56e09ab0b", + "3": "0x4530c0483a1633c7a1c97d2c53721caff2caaaaf", + "42": "0x35dd2932454449b14cee11a94d3674a936d5d7b2", + "50": "0x48bacb9266a570d521063ef5dd96e61686dbe788", + } + + null_address = "0x0000000000000000000000000000000000000000" + + eip191_header = b"\x19\x01" + + eip712_domain_separator_schema_hash = keccak( + b"EIP712Domain(string name,string version,address verifyingContract)" + ) + + eip712_domain_struct_header = ( + eip712_domain_separator_schema_hash + + keccak(b"0x Protocol") + + keccak(b"2") + ) + + eip712_order_schema_hash = keccak( + b"Order(" + + b"address makerAddress," + + b"address takerAddress," + + b"address feeRecipientAddress," + + b"address senderAddress," + + b"uint256 makerAssetAmount," + + b"uint256 takerAssetAmount," + + b"uint256 makerFee," + + b"uint256 takerFee," + + b"uint256 expirationTimeSeconds," + + b"uint256 salt," + + b"bytes makerAssetData," + + b"bytes takerAssetData" + + b")" + ) + + class SignatureType(Enum): + """Enumeration of known signature types.""" + + ILLEGAL = 0 + INVALID = auto() + EIP712 = auto() + ETH_SIGN = auto() + WALLET = auto() + VALIDATOR = auto() + PRE_SIGNED = auto() + N_SIGNATURE_TYPES = auto() + + +class Order(TypedDict): # pylint: disable=too-many-instance-attributes + """Object representation of a 0x order.""" + + makerAddress: str + takerAddress: str + feeRecipientAddress: str + senderAddress: str + makerAssetAmount: str + takerAssetAmount: str + makerFee: str + takerFee: str + expirationTimeSeconds: str + salt: str + makerAssetData: str + takerAssetData: str + exchangeAddress: str + + +def make_empty_order() -> Order: + """Construct an empty order. + + Initializes all strings to "0x0000000000000000000000000000000000000000" + and all numbers to 0. + """ + return { + "makerAddress": _Constants.null_address, + "takerAddress": _Constants.null_address, + "senderAddress": _Constants.null_address, + "feeRecipientAddress": _Constants.null_address, + "makerAssetData": _Constants.null_address, + "takerAssetData": _Constants.null_address, + "salt": "0", + "makerFee": "0", + "takerFee": "0", + "makerAssetAmount": "0", + "takerAssetAmount": "0", + "expirationTimeSeconds": "0", + "exchangeAddress": _Constants.null_address, + } + + +def generate_order_hash_hex(order: Order) -> str: + """Calculate the hash of the given order as a hexadecimal string. + + :param order: The order to be hashed. Must conform to `the 0x order JSON schema <https://github.com/0xProject/0x-monorepo/blob/development/packages/json-schemas/schemas/order_schema.json>`_. + :param exchange_address: The address to which the 0x Exchange smart + contract has been deployed. + :rtype: A string, of ASCII hex digits, representing the order hash. + + >>> generate_order_hash_hex( + ... { + ... 'makerAddress': "0x0000000000000000000000000000000000000000", + ... 'takerAddress': "0x0000000000000000000000000000000000000000", + ... 'feeRecipientAddress': "0x0000000000000000000000000000000000000000", + ... 'senderAddress': "0x0000000000000000000000000000000000000000", + ... 'makerAssetAmount': "1000000000000000000", + ... 'takerAssetAmount': "1000000000000000000", + ... 'makerFee': "0", + ... 'takerFee': "0", + ... 'expirationTimeSeconds': "12345", + ... 'salt': "12345", + ... 'makerAssetData': "0x0000000000000000000000000000000000000000", + ... 'takerAssetData': "0x0000000000000000000000000000000000000000", + ... 'exchangeAddress': "0x0000000000000000000000000000000000000000", + ... }, + ... ) + '55eaa6ec02f3224d30873577e9ddd069a288c16d6fb407210eecbc501fa76692' + """ # noqa: E501 (line too long) + assert_valid(order, "/orderSchema") + + def pad_20_bytes_to_32(twenty_bytes: bytes): + return bytes(12) + twenty_bytes + + def int_to_32_big_endian_bytes(i: int): + return i.to_bytes(32, byteorder="big") + + eip712_domain_struct_hash = keccak( + _Constants.eip712_domain_struct_header + + pad_20_bytes_to_32(to_bytes(hexstr=order["exchangeAddress"])) + ) + + eip712_order_struct_hash = keccak( + _Constants.eip712_order_schema_hash + + pad_20_bytes_to_32(to_bytes(hexstr=order["makerAddress"])) + + pad_20_bytes_to_32(to_bytes(hexstr=order["takerAddress"])) + + pad_20_bytes_to_32(to_bytes(hexstr=order["feeRecipientAddress"])) + + pad_20_bytes_to_32(to_bytes(hexstr=order["senderAddress"])) + + int_to_32_big_endian_bytes(int(order["makerAssetAmount"])) + + int_to_32_big_endian_bytes(int(order["takerAssetAmount"])) + + int_to_32_big_endian_bytes(int(order["makerFee"])) + + int_to_32_big_endian_bytes(int(order["takerFee"])) + + int_to_32_big_endian_bytes(int(order["expirationTimeSeconds"])) + + int_to_32_big_endian_bytes(int(order["salt"])) + + keccak(to_bytes(hexstr=order["makerAssetData"])) + + keccak(to_bytes(hexstr=order["takerAssetData"])) + ) + + return keccak( + _Constants.eip191_header + + eip712_domain_struct_hash + + eip712_order_struct_hash + ).hex() + + +def is_valid_signature( + provider: BaseProvider, data: str, signature: str, signer_address: str +) -> Tuple[bool, str]: + """Check the validity of the supplied signature. + + Check if the supplied ``signature`` corresponds to signing ``data`` with + the private key corresponding to ``signer_address``. + + :param provider: A Web3 provider able to access the 0x Exchange contract. + :param data: The hex encoded data signed by the supplied signature. + :param signature: The hex encoded signature. + :param signer_address: The hex encoded address that signed the data to + produce the supplied signature. + :rtype: Tuple consisting of a boolean and a string. Boolean is true if + valid, false otherwise. If false, the string describes the reason. + + >>> is_valid_signature( + ... Web3.HTTPProvider("http://127.0.0.1:8545"), + ... '0x6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222b0', + ... '0x1B61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc3340349190569279751135161d22529dc25add4f6069af05be04cacbda2ace225403', + ... '0x5409ed021d9299bf6814279a6a1411a7e866a631', + ... ) + (True, '') + """ # noqa: E501 (line too long) + assert_is_provider(provider, "provider") + assert_is_hex_string(data, "data") + assert_is_hex_string(signature, "signature") + assert_is_address(signer_address, "signer_address") + + web3_instance = Web3(provider) + # false positive from pylint: disable=no-member + network_id = web3_instance.net.version + contract_address = _Constants.network_to_exchange_addr[network_id] + # false positive from pylint: disable=no-member + contract: datatypes.Contract = web3_instance.eth.contract( + address=to_checksum_address(contract_address), + abi=_Constants.contract_name_to_abi("Exchange"), + ) + try: + return ( + contract.call().isValidSignature( + data, to_checksum_address(signer_address), signature + ), + "", + ) + except web3.exceptions.BadFunctionCallOutput as exception: + known_revert_reasons = [ + "LENGTH_GREATER_THAN_0_REQUIRED", + "SIGNATURE_ILLEGAL", + "SIGNATURE_UNSUPPORTED", + "LENGTH_0_REQUIRED", + "LENGTH_65_REQUIRED", + ] + for known_revert_reason in known_revert_reasons: + if known_revert_reason in str(exception): + return (False, known_revert_reason) + return (False, f"Unknown: {exception}") + + +class ECSignature(TypedDict): + """Object representation of an elliptic curve signature's parameters.""" + + v: int + r: str + s: str + + +def _parse_signature_hex_as_vrs(signature_hex: str) -> ECSignature: + """Parse signature hex as a concatentation of EC parameters ordered V, R, S. + + >>> _parse_signature_hex_as_vrs('0x1b117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d872871137feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b03') + {'v': 27, 'r': '117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d87287113', 's': '7feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b'} + """ # noqa: E501 (line too long) + signature: ECSignature = { + "v": int(signature_hex[2:4], 16), + "r": signature_hex[4:68], + "s": signature_hex[68:132], + } + if signature["v"] == 0 or signature["v"] == 1: + signature["v"] = signature["v"] + 27 + return signature + + +def _parse_signature_hex_as_rsv(signature_hex: str) -> ECSignature: + """Parse signature hex as a concatentation of EC parameters ordered R, S, V. + + >>> _parse_signature_hex_as_rsv('0x117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d872871137feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b00') + {'r': '117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d87287113', 's': '7feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b', 'v': 27} + """ # noqa: E501 (line too long) + signature: ECSignature = { + "r": signature_hex[2:66], + "s": signature_hex[66:130], + "v": int(signature_hex[130:132], 16), + } + if signature["v"] == 0 or signature["v"] == 1: + signature["v"] = signature["v"] + 27 + return signature + + +def _convert_ec_signature_to_vrs_hex(signature: ECSignature) -> str: + """Convert elliptic curve signature object to hex hash string. + + >>> _convert_ec_signature_to_vrs_hex( + ... { + ... 'r': '117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d87287113', + ... 's': '7feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b', + ... 'v': 27 + ... } + ... ) + '0x1b117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d872871137feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b' + """ # noqa: E501 (line too long) + return ( + "0x" + + signature["v"].to_bytes(1, byteorder="big").hex() + + signature["r"] + + signature["s"] + ) + + +def sign_hash( + provider: BaseProvider, signer_address: str, hash_hex: str +) -> str: + """Sign a message with the given hash, and return the signature. + + :param provider: A Web3 provider. + :param signer_address: The address of the signing account. + :param hash_hex: A hex string representing the hash, like that returned + from `generate_order_hash_hex()`. + :rtype: A string, of ASCII hex digits, representing the signature. + + >>> provider = Web3.HTTPProvider("http://127.0.0.1:8545") + >>> sign_hash( + ... provider, + ... Web3(provider).personal.listAccounts[0], + ... '0x34decbedc118904df65f379a175bb39ca18209d6ce41d5ed549d54e6e0a95004', + ... ) + '0x1b117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d872871137feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b03' + """ # noqa: E501 (line too long) + assert_is_provider(provider, "provider") + assert_is_address(signer_address, "signer_address") + assert_is_hex_string(hash_hex, "hash_hex") + + web3_instance = Web3(provider) + # false positive from pylint: disable=no-member + signature = web3_instance.eth.sign( # type: ignore + signer_address, hexstr=hash_hex.replace("0x", "") + ).hex() + + valid_v_param_values = [27, 28] + + # 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) return the signature params in + # different orders. In order to support all client implementations, we + # parse the signature in both ways, and evaluate if either one is a valid + # signature. r + s + v is the most prevalent format from eth_sign, so we + # attempt this first. + + ec_signature = _parse_signature_hex_as_rsv(signature) + if ec_signature["v"] in valid_v_param_values: + signature_as_vrst_hex = ( + _convert_ec_signature_to_vrs_hex(ec_signature) + + _Constants.SignatureType.ETH_SIGN.value.to_bytes( + 1, byteorder="big" + ).hex() + ) + + (valid, _) = is_valid_signature( + provider, hash_hex, signature_as_vrst_hex, signer_address + ) + + if valid is True: + return signature_as_vrst_hex + + ec_signature = _parse_signature_hex_as_vrs(signature) + if ec_signature["v"] in valid_v_param_values: + signature_as_vrst_hex = ( + _convert_ec_signature_to_vrs_hex(ec_signature) + + _Constants.SignatureType.ETH_SIGN.value.to_bytes( + 1, byteorder="big" + ).hex() + ) + (valid, _) = is_valid_signature( + provider, hash_hex, signature_as_vrst_hex, signer_address + ) + + if valid is True: + return signature_as_vrst_hex + + raise RuntimeError( + "Signature returned from web3 provider is in an unknown format." + + " Attempted to parse as RSV and as VRS." + ) diff --git a/python-packages/order_utils/src/zero_ex/order_utils/asset_data_utils.py b/python-packages/order_utils/src/zero_ex/order_utils/asset_data_utils.py index e6f9a07c1..fab7479d2 100644 --- a/python-packages/order_utils/src/zero_ex/order_utils/asset_data_utils.py +++ b/python-packages/order_utils/src/zero_ex/order_utils/asset_data_utils.py @@ -47,7 +47,6 @@ def encode_erc20_asset_data(token_address: str) -> str: def decode_erc20_asset_data(asset_data: str) -> ERC20AssetData: - # docstring considered all one line by pylint: disable=line-too-long """Decode an ERC20 asset data hex string. :param asset_data: String produced by prior call to encode_erc20_asset_data() @@ -82,7 +81,6 @@ def decode_erc20_asset_data(asset_data: str) -> ERC20AssetData: def encode_erc721_asset_data(token_address: str, token_id: int) -> str: - # docstring considered all one line by pylint: disable=line-too-long """Encode an ERC721 asset data hex string. :param token_address: the ERC721 token's contract address. @@ -105,7 +103,6 @@ def encode_erc721_asset_data(token_address: str, token_id: int) -> str: def decode_erc721_asset_data(asset_data: str) -> ERC721AssetData: - # docstring considered all one line by pylint: disable=line-too-long """Decode an ERC721 asset data hex string. >>> decode_erc721_asset_data('0x025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000001') @@ -121,7 +118,6 @@ def decode_erc721_asset_data(asset_data: str) -> ERC721AssetData: ) asset_proxy_id: str = asset_data[0:SELECTOR_LENGTH] - # prefer `black` formatting. pylint: disable=C0330 if asset_proxy_id != abi_utils.method_id( "ERC721Token", ["address", "uint256"] ): 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 deleted file mode 100644 index 12525ba88..000000000 --- a/python-packages/order_utils/src/zero_ex/order_utils/signature_utils.py +++ /dev/null @@ -1,88 +0,0 @@ -"""Signature utilities.""" - -from typing import Dict, Tuple -import json -from pkg_resources import resource_string - -from eth_utils import is_address, to_checksum_address -from web3 import Web3 -import web3.exceptions -from web3.utils import datatypes - -from zero_ex.dev_utils.type_assertions import assert_is_hex_string - - -# prefer `black` formatting. pylint: disable=C0330 -EXCHANGE_ABI = json.loads( - resource_string("zero_ex.contract_artifacts", "artifacts/Exchange.json") -)["compilerOutput"]["abi"] - -network_to_exchange_addr: Dict[str, str] = { - "1": "0x4f833a24e1f95d70f028921e27040ca56e09ab0b", - "3": "0x4530c0483a1633c7a1c97d2c53721caff2caaaaf", - "42": "0x35dd2932454449b14cee11a94d3674a936d5d7b2", - "50": "0x48bacb9266a570d521063ef5dd96e61686dbe788", -} - - -# prefer `black` formatting. pylint: disable=C0330 -def is_valid_signature( - provider: Web3.HTTPProvider, data: str, signature: str, signer_address: str -) -> Tuple[bool, str]: - # docstring considered all one line by pylint: disable=line-too-long - """Check the validity of the supplied signature. - - Check if the supplied ``signature`` corresponds to signing ``data`` with - the private key corresponding to ``signer_address``. - - :param provider: A Web3 provider able to access the 0x Exchange contract. - :param data: The hex encoded data signed by the supplied signature. - :param signature: The hex encoded signature. - :param signer_address: The hex encoded address that signed the data to - produce the supplied signature. - :rtype: Boolean indicating whether the given signature is valid. - - >>> is_valid_signature( - ... Web3.HTTPProvider("http://127.0.0.1:8545"), - ... '0x6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222b0', - ... '0x1B61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc3340349190569279751135161d22529dc25add4f6069af05be04cacbda2ace225403', - ... '0x5409ed021d9299bf6814279a6a1411a7e866a631', - ... ) - (True, '') - """ # noqa: E501 (line too long) - # TODO: make this provider check more flexible. pylint: disable=fixme - # https://app.asana.com/0/684263176955174/901300863045491/f - if not isinstance(provider, Web3.HTTPProvider): - raise TypeError("provider is not a Web3.HTTPProvider") - assert_is_hex_string(data, "data") - assert_is_hex_string(signature, "signature") - assert_is_hex_string(signer_address, "signer_address") - if not is_address(signer_address): - raise ValueError("signer_address is not a valid address") - - web3_instance = Web3(provider) - # false positive from pylint: disable=no-member - network_id = web3_instance.net.version - contract_address = network_to_exchange_addr[network_id] - # false positive from pylint: disable=no-member - contract: datatypes.Contract = web3_instance.eth.contract( - address=to_checksum_address(contract_address), abi=EXCHANGE_ABI - ) - try: - return ( - contract.call().isValidSignature( - data, to_checksum_address(signer_address), signature - ), - "", - ) - except web3.exceptions.BadFunctionCallOutput as exception: - known_revert_reasons = [ - "LENGTH_GREATER_THAN_0_REQUIRED", - "SIGNATURE_UNSUPPORTED", - "LENGTH_0_REQUIRED", - "LENGTH_65_REQUIRED", - ] - for known_revert_reason in known_revert_reasons: - if known_revert_reason in str(exception): - return (False, known_revert_reason) - return (False, f"Unknown: {exception}") diff --git a/python-packages/order_utils/stubs/jsonschema/__init__.pyi b/python-packages/order_utils/stubs/jsonschema/__init__.pyi new file mode 100644 index 000000000..762b58b22 --- /dev/null +++ b/python-packages/order_utils/stubs/jsonschema/__init__.pyi @@ -0,0 +1,5 @@ +from typing import Any, Dict + +class RefResolver: pass + +def validate(instance: Any, schema: Dict, cls=None, *args, **kwargs) -> None: pass diff --git a/python-packages/order_utils/stubs/jsonschema/exceptions.pyi b/python-packages/order_utils/stubs/jsonschema/exceptions.pyi new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python-packages/order_utils/stubs/jsonschema/exceptions.pyi diff --git a/python-packages/order_utils/stubs/sha3/__init__.pyi b/python-packages/order_utils/stubs/sha3/__init__.pyi new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python-packages/order_utils/stubs/sha3/__init__.pyi diff --git a/python-packages/order_utils/stubs/web3/__init__.pyi b/python-packages/order_utils/stubs/web3/__init__.pyi index fcecc7434..b2af95475 100644 --- a/python-packages/order_utils/stubs/web3/__init__.pyi +++ b/python-packages/order_utils/stubs/web3/__init__.pyi @@ -1,12 +1,14 @@ from typing import Dict, Optional, Union from web3.utils import datatypes +from web3.providers.base import BaseProvider class Web3: - class HTTPProvider: ... + class HTTPProvider(BaseProvider): + ... - def __init__(self, provider: HTTPProvider) -> None: ... + def __init__(self, provider: BaseProvider) -> None: ... @staticmethod def sha3( diff --git a/python-packages/order_utils/stubs/web3/providers/__init__.pyi b/python-packages/order_utils/stubs/web3/providers/__init__.pyi new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python-packages/order_utils/stubs/web3/providers/__init__.pyi diff --git a/python-packages/order_utils/stubs/web3/providers/base.pyi b/python-packages/order_utils/stubs/web3/providers/base.pyi new file mode 100644 index 000000000..82ca9e3da --- /dev/null +++ b/python-packages/order_utils/stubs/web3/providers/base.pyi @@ -0,0 +1,2 @@ +class BaseProvider: + ... diff --git a/python-packages/order_utils/test/test_doctest.py b/python-packages/order_utils/test/test_doctest.py index 2b0350ac0..f692b3b6c 100644 --- a/python-packages/order_utils/test/test_doctest.py +++ b/python-packages/order_utils/test/test_doctest.py @@ -8,7 +8,6 @@ import zero_ex def test_all_doctests(): """Gather zero_ex.* modules and doctest them.""" - # prefer `black` formatting. pylint: disable=bad-continuation for (importer, modname, _) in pkgutil.walk_packages( path=zero_ex.__path__, prefix="zero_ex." ): diff --git a/python-packages/order_utils/test/test_generate_order_hash_hex.py b/python-packages/order_utils/test/test_generate_order_hash_hex.py new file mode 100644 index 000000000..6869a40ed --- /dev/null +++ b/python-packages/order_utils/test/test_generate_order_hash_hex.py @@ -0,0 +1,12 @@ +"""Test zero_ex.order_utils.get_order_hash_hex().""" + +from zero_ex.order_utils import generate_order_hash_hex, make_empty_order + + +def test_get_order_hash_hex__empty_order(): + """Test the hashing of an uninitialized order.""" + expected_hash_hex = ( + "faa49b35faeb9197e9c3ba7a52075e6dad19739549f153b77dfcf59408a4b422" + ) + actual_hash_hex = generate_order_hash_hex(make_empty_order()) + assert actual_hash_hex == expected_hash_hex diff --git a/python-packages/order_utils/test/test_signature_utils.py b/python-packages/order_utils/test/test_signature_utils.py index b688e03a1..c5acc9d62 100644 --- a/python-packages/order_utils/test/test_signature_utils.py +++ b/python-packages/order_utils/test/test_signature_utils.py @@ -3,7 +3,7 @@ import pytest from web3 import Web3 -from zero_ex.order_utils.signature_utils import is_valid_signature +from zero_ex.order_utils import is_valid_signature def test_is_valid_signature__provider_wrong_type(): @@ -3847,6 +3847,11 @@ bowser@^1.7.3, bowser@^1.9.3: resolved "https://registry.yarnpkg.com/bowser/-/bowser-1.9.3.tgz#6643ae4d783f31683f6d23156976b74183862162" integrity sha512-/gp96UlcFw5DbV2KQPCqTqi0Mb9gZRyDAHiDsGEH+4B/KOQjeoE5lM1PxlVX8DQDvfEfitmC1rW2Oy8fk/XBDg== +bowser@^1.9.4: + version "1.9.4" + resolved "https://registry.npmjs.org/bowser/-/bowser-1.9.4.tgz#890c58a2813a9d3243704334fa81b96a5c150c9a" + integrity sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ== + boxen@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" @@ -9523,7 +9528,7 @@ ip-regex@^2.1.0: ip@^1.1.0, ip@^1.1.5: version "1.1.5" - resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + resolved "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= ipaddr.js@1.6.0: |