diff options
author | Fabio Berger <me@fabioberger.com> | 2018-04-09 13:34:36 +0800 |
---|---|---|
committer | Fabio Berger <me@fabioberger.com> | 2018-04-09 13:34:36 +0800 |
commit | e05b55d4a5698d3e936e7164ed69d9417d12cd12 (patch) | |
tree | 1f9be2d21d21cc915687f3606b2dad9e8a4add8e | |
parent | 6f24337a5309bc4b06d0a91eac3af7566bde5754 (diff) | |
parent | 073bf738ddb271b6b4158798baf4cac3cb0608e9 (diff) | |
download | dexon-sol-tools-e05b55d4a5698d3e936e7164ed69d9417d12cd12.tar dexon-sol-tools-e05b55d4a5698d3e936e7164ed69d9417d12cd12.tar.gz dexon-sol-tools-e05b55d4a5698d3e936e7164ed69d9417d12cd12.tar.bz2 dexon-sol-tools-e05b55d4a5698d3e936e7164ed69d9417d12cd12.tar.lz dexon-sol-tools-e05b55d4a5698d3e936e7164ed69d9417d12cd12.tar.xz dexon-sol-tools-e05b55d4a5698d3e936e7164ed69d9417d12cd12.tar.zst dexon-sol-tools-e05b55d4a5698d3e936e7164ed69d9417d12cd12.zip |
merge development
44 files changed, 1417 insertions, 306 deletions
diff --git a/packages/0x.js/README.md b/packages/0x.js/README.md index 5b17dac8b..46848cbdd 100644 --- a/packages/0x.js/README.md +++ b/packages/0x.js/README.md @@ -1,5 +1,9 @@ ## 0x.js +A TypeScript/Javascript library for interacting with the 0x protocol. + +### Read the [Documentation](0xproject.com/docs/0xjs). + ## Installation 0x.js ships as both a [UMD](https://github.com/umdjs/umd) module and a [CommonJS](https://en.wikipedia.org/wiki/CommonJS) package. @@ -38,10 +42,66 @@ Download the UMD module from our [releases page](https://github.com/0xProject/0x <script type="text/javascript" src="0x.js"></script> ``` -## Documentation +## Contributing + +We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository. + +Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. + +### Install dependencies + +If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them: + +```bash +yarn config set workspaces-experimental true +``` + +Then install dependencies + +```bash +yarn install +``` + +### Build + +If this is your **first** time building this package, you must first build **all** packages within the monorepo. This is because packages that depend on other packages located inside this monorepo are symlinked when run from **within** the monorepo. This allows you to make changes across multiple packages without first publishing dependent packages to NPM. To build all packages, run the following from the monorepo root directory: + +```bash +yarn lerna:rebuild +``` + +Or continuously rebuild on change: + +```bash +yarn dev +``` + +You can also build this specific package by running the following from within its directory: + +```bash +yarn build +``` + +or continuously rebuild on change: + +```bash +yarn build:watch +``` + +### Clean + +```bash +yarn clean +``` + +### Lint -Extensive documentation of 0x.js can be found on [our website][docs-url]. +```bash +yarn lint +``` + +### Run Tests -[website-url]: https://0xproject.com/ -[whitepaper-url]: https://0xproject.com/pdfs/0x_white_paper.pdf -[docs-url]: https://0xproject.com/docs/0xjs +```bash +yarn test +``` diff --git a/packages/abi-gen/README.md b/packages/abi-gen/README.md index 899b4845e..301bd709f 100644 --- a/packages/abi-gen/README.md +++ b/packages/abi-gen/README.md @@ -57,3 +57,61 @@ See the [type definition](https://github.com/0xProject/0x-monorepo/tree/developm ## Output files Output files will be generated within an output folder with names converted to camel case and taken from abi file names. If you already have some files in that folder they will be overwritten. + +## Contributing + +We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository. + +Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. + +### Install dependencies + +If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them: + +```bash +yarn config set workspaces-experimental true +``` + +Then install dependencies + +```bash +yarn install +``` + +### Build + +If this is your **first** time building this package, you must first build **all** packages within the monorepo. This is because packages that depend on other packages located inside this monorepo are symlinked when run from **within** the monorepo. This allows you to make changes across multiple packages without first publishing dependent packages to NPM. To build all packages, run the following from the monorepo root directory: + +```bash +yarn lerna:rebuild +``` + +Or continuously rebuild on change: + +```bash +yarn dev +``` + +You can also build this specific package by running the following from within its directory: + +```bash +yarn build +``` + +or continuously rebuild on change: + +```bash +yarn build:watch +``` + +### Clean + +```bash +yarn clean +``` + +### Lint + +```bash +yarn lint +``` diff --git a/packages/assert/README.md b/packages/assert/README.md index 7f80e7e5d..598fee613 100644 --- a/packages/assert/README.md +++ b/packages/assert/README.md @@ -26,11 +26,11 @@ If your project is in [TypeScript](https://www.typescriptlang.org/), add the fol ## Contributing -We strongly encourage that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository. +We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository. Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. -### Install Dependencies +### Install dependencies If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them: @@ -46,10 +46,36 @@ yarn install ### Build +If this is your **first** time building this package, you must first build **all** packages within the monorepo. This is because packages that depend on other packages located inside this monorepo are symlinked when run from **within** the monorepo. This allows you to make changes across multiple packages without first publishing dependent packages to NPM. To build all packages, run the following from the monorepo root directory: + +```bash +yarn lerna:rebuild +``` + +Or continuously rebuild on change: + +```bash +yarn dev +``` + +You can also build this specific package by running the following from within its directory: + ```bash yarn build ``` +or continuously rebuild on change: + +```bash +yarn build:watch +``` + +### Clean + +```bash +yarn clean +``` + ### Lint ```bash diff --git a/packages/connect/README.md b/packages/connect/README.md index 7087214d6..a57fefa59 100644 --- a/packages/connect/README.md +++ b/packages/connect/README.md @@ -23,11 +23,11 @@ If your project is in [TypeScript](https://www.typescriptlang.org/), add the fol ## Contributing -We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository. +We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository. Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. -### Install Dependencies +### Install dependencies If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them: @@ -43,10 +43,36 @@ yarn install ### Build +If this is your **first** time building this package, you must first build **all** packages within the monorepo. This is because packages that depend on other packages located inside this monorepo are symlinked when run from **within** the monorepo. This allows you to make changes across multiple packages without first publishing dependent packages to NPM. To build all packages, run the following from the monorepo root directory: + +```bash +yarn lerna:rebuild +``` + +Or continuously rebuild on change: + +```bash +yarn dev +``` + +You can also build this specific package by running the following from within its directory: + ```bash yarn build ``` +or continuously rebuild on change: + +```bash +yarn build:watch +``` + +### Clean + +```bash +yarn clean +``` + ### Lint ```bash diff --git a/packages/contracts/README.md b/packages/contracts/README.md index 7628b057b..286ef019c 100644 --- a/packages/contracts/README.md +++ b/packages/contracts/README.md @@ -34,11 +34,25 @@ yarn install ### Build +If this is your **first** time building this package, you must first build **all** packages within the monorepo. This is because packages that depend on other packages located inside this monorepo are symlinked when run from **within** the monorepo. This allows you to make changes across multiple packages without first publishing dependent packages to NPM. To build all packages, run the following from the monorepo root directory: + +```bash +yarn lerna:rebuild +``` + +Or continuously rebuild on change: + +```bash +yarn dev +``` + +You can also build this specific package by running the following from within its directory: + ```bash yarn build ``` -or +or continuously rebuild on change: ```bash yarn build:watch diff --git a/packages/deployer/README.md b/packages/deployer/README.md index 812e8c31b..d8b049bdf 100644 --- a/packages/deployer/README.md +++ b/packages/deployer/README.md @@ -41,11 +41,11 @@ var Compiler = require('@0xproject/deployer').Compiler; ## Contributing -We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository. +We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository. Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. -### Install Dependencies +### Install dependencies If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them: @@ -61,16 +61,36 @@ yarn install ### Build +If this is your **first** time building this package, you must first build **all** packages within the monorepo. This is because packages that depend on other packages located inside this monorepo are symlinked when run from **within** the monorepo. This allows you to make changes across multiple packages without first publishing dependent packages to NPM. To build all packages, run the following from the monorepo root directory: + +```bash +yarn lerna:rebuild +``` + +Or continuously rebuild on change: + +```bash +yarn dev +``` + +You can also build this specific package by running the following from within its directory: + ```bash yarn build ``` -or +or continuously rebuild on change: ```bash yarn build:watch ``` +### Clean + +```bash +yarn clean +``` + ### Lint ```bash diff --git a/packages/dev-utils/README.md b/packages/dev-utils/README.md index d7e580d9f..2b183f1c7 100644 --- a/packages/dev-utils/README.md +++ b/packages/dev-utils/README.md @@ -26,3 +26,67 @@ If your project is in [TypeScript](https://www.typescriptlang.org/), add the fol "typeRoots": ["node_modules/@0xproject/typescript-typings/types", "node_modules/@types"], } ``` + +## Contributing + +We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository. + +Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. + +### Install dependencies + +If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them: + +```bash +yarn config set workspaces-experimental true +``` + +Then install dependencies + +```bash +yarn install +``` + +### Build + +If this is your **first** time building this package, you must first build **all** packages within the monorepo. This is because packages that depend on other packages located inside this monorepo are symlinked when run from **within** the monorepo. This allows you to make changes across multiple packages without first publishing dependent packages to NPM. To build all packages, run the following from the monorepo root directory: + +```bash +yarn lerna:rebuild +``` + +Or continuously rebuild on change: + +```bash +yarn dev +``` + +You can also build this specific package by running the following from within its directory: + +```bash +yarn build +``` + +or continuously rebuild on change: + +```bash +yarn build:watch +``` + +### Clean + +```bash +yarn clean +``` + +### Lint + +```bash +yarn lint +``` + +### Run Tests + +```bash +yarn test +``` diff --git a/packages/json-schemas/README.md b/packages/json-schemas/README.md index 980d17416..e8cece63b 100644 --- a/packages/json-schemas/README.md +++ b/packages/json-schemas/README.md @@ -32,11 +32,11 @@ If your project is in [TypeScript](https://www.typescriptlang.org/), add the fol ## Contributing -We strongly encourage that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository. +We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository. Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. -### Install Dependencies +### Install dependencies If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them: @@ -52,16 +52,36 @@ yarn install ### Build +If this is your **first** time building this package, you must first build **all** packages within the monorepo. This is because packages that depend on other packages located inside this monorepo are symlinked when run from **within** the monorepo. This allows you to make changes across multiple packages without first publishing dependent packages to NPM. To build all packages, run the following from the monorepo root directory: + +```bash +yarn lerna:rebuild +``` + +Or continuously rebuild on change: + +```bash +yarn dev +``` + +You can also build this specific package by running the following from within its directory: + ```bash yarn build ``` -or +or continuously rebuild on change: ```bash yarn build:watch ``` +### Clean + +```bash +yarn clean +``` + ### Lint ```bash diff --git a/packages/monorepo-scripts/README.md b/packages/monorepo-scripts/README.md index dbdfdf135..a740ba28a 100644 --- a/packages/monorepo-scripts/README.md +++ b/packages/monorepo-scripts/README.md @@ -16,11 +16,11 @@ This will list out any dependencies that differ in versions between packages. ## Contributing -We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository. +We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository. Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. -### Install Dependencies +### Install dependencies If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them: @@ -36,11 +36,25 @@ yarn install ### Build +If this is your **first** time building this package, you must first build **all** packages within the monorepo. This is because packages that depend on other packages located inside this monorepo are symlinked when run from **within** the monorepo. This allows you to make changes across multiple packages without first publishing dependent packages to NPM. To build all packages, run the following from the monorepo root directory: + +```bash +yarn lerna:rebuild +``` + +Or continuously rebuild on change: + +```bash +yarn dev +``` + +You can also build this specific package by running the following from within its directory: + ```bash yarn build ``` -or +or continuously rebuild on change: ```bash yarn build:watch @@ -57,3 +71,9 @@ yarn clean ```bash yarn lint ``` + +### Run Tests + +```bash +yarn test +``` diff --git a/packages/react-docs-example/README.md b/packages/react-docs-example/README.md index 7b2547b20..ff821dba8 100644 --- a/packages/react-docs-example/README.md +++ b/packages/react-docs-example/README.md @@ -20,7 +20,19 @@ Then install dependencies yarn install ``` -#### Start the dev server +### Initial setup + +The **first** time you work with this package, you must build **all** packages within the monorepo. This is because packages that depend on other packages located inside this monorepo are symlinked when run from **within** the monorepo. This allows you to make changes across multiple packages without first publishing dependent packages to NPM. To build all packages, run the following from the monorepo root directory: + +```bash +yarn lerna:rebuild +``` + +Note: If you move this package out of the monorepo, it will work without this step. Make sure you copy it out on the `master` branch since the `development` version might rely on not-yet published changes to other packages. + +### Run dev server + +The the `react-docs-example` root directory, run: ```bash yarn dev @@ -42,6 +54,12 @@ yarn deploy_example yarn build ``` +### Clean + +```bash +yarn clean +``` + ### Lint ```bash diff --git a/packages/react-docs/README.md b/packages/react-docs/README.md index 9be370f10..5a46b84f0 100644 --- a/packages/react-docs/README.md +++ b/packages/react-docs/README.md @@ -51,11 +51,11 @@ Feel free to contribute to these improvements! ## Contributing -We strongly encourage the community to help us make improvements. To report bugs within this package, please create an issue in this repository. +We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository. Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. -### Install Dependencies +### Install dependencies If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them: @@ -71,10 +71,36 @@ yarn install ### Build +If this is your **first** time building this package, you must first build **all** packages within the monorepo. This is because packages that depend on other packages located inside this monorepo are symlinked when run from **within** the monorepo. This allows you to make changes across multiple packages without first publishing dependent packages to NPM. To build all packages, run the following from the monorepo root directory: + +```bash +yarn lerna:rebuild +``` + +Or continuously rebuild on change: + +```bash +yarn dev +``` + +You can also build this specific package by running the following from within its directory: + ```bash yarn build ``` +or continuously rebuild on change: + +```bash +yarn build:watch +``` + +### Clean + +```bash +yarn clean +``` + ### Lint ```bash diff --git a/packages/react-shared/README.md b/packages/react-shared/README.md index 9165cf78d..0b4188180 100644 --- a/packages/react-shared/README.md +++ b/packages/react-shared/README.md @@ -18,11 +18,11 @@ If your project is in [TypeScript](https://www.typescriptlang.org/), add the fol ## Contributing -We strongly encourage that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository. +We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository. Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. -### Install Dependencies +### Install dependencies If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them: @@ -38,18 +38,38 @@ yarn install ### Build +If this is your **first** time building this package, you must first build **all** packages within the monorepo. This is because packages that depend on other packages located inside this monorepo are symlinked when run from **within** the monorepo. This allows you to make changes across multiple packages without first publishing dependent packages to NPM. To build all packages, run the following from the monorepo root directory: + +```bash +yarn lerna:rebuild +``` + +Or continuously rebuild on change: + +```bash +yarn dev +``` + +You can also build this specific package by running the following from within its directory: + ```bash yarn build ``` -### Lint +or continuously rebuild on change: ```bash -yarn lint +yarn build:watch +``` + +### Clean + +```bash +yarn clean ``` -### Run Tests +### Lint ```bash -yarn test +yarn lint ``` diff --git a/packages/sol-cov/README.md b/packages/sol-cov/README.md index 5591b9731..8cc7dca6c 100644 --- a/packages/sol-cov/README.md +++ b/packages/sol-cov/README.md @@ -24,11 +24,11 @@ var CoverageSubprovider = require('@0xproject/sol-cov').CoverageSubprovider; ## Contributing -We strongly encourage the community to help us make improvements. To report bugs within this package, please create an issue in this repository. +We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository. Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. -### Install Dependencies +### Install dependencies If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them: @@ -42,8 +42,46 @@ Then install dependencies yarn install ``` +### Build + +If this is your **first** time building this package, you must first build **all** packages within the monorepo. This is because packages that depend on other packages located inside this monorepo are symlinked when run from **within** the monorepo. This allows you to make changes across multiple packages without first publishing dependent packages to NPM. To build all packages, run the following from the monorepo root directory: + +```bash +yarn lerna:rebuild +``` + +Or continuously rebuild on change: + +```bash +yarn dev +``` + +You can also build this specific package by running the following from within its directory: + +```bash +yarn build +``` + +or continuously rebuild on change: + +```bash +yarn build:watch +``` + +### Clean + +```bash +yarn clean +``` + ### Lint ```bash yarn lint ``` + +### Run Tests + +```bash +yarn test +``` diff --git a/packages/sra-report/README.md b/packages/sra-report/README.md index 299d575d8..806ac3bf4 100644 --- a/packages/sra-report/README.md +++ b/packages/sra-report/README.md @@ -87,11 +87,11 @@ In order to provide a custom environment to the tool, perform the following step ## Contributing -We strongly encourage that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository. +We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository. Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. -### Install Dependencies +### Install dependencies If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them: @@ -107,10 +107,36 @@ yarn install ### Build +If this is your **first** time building this package, you must first build **all** packages within the monorepo. This is because packages that depend on other packages located inside this monorepo are symlinked when run from **within** the monorepo. This allows you to make changes across multiple packages without first publishing dependent packages to NPM. To build all packages, run the following from the monorepo root directory: + +```bash +yarn lerna:rebuild +``` + +Or continuously rebuild on change: + +```bash +yarn dev +``` + +You can also build this specific package by running the following from within its directory: + ```bash yarn build ``` +or continuously rebuild on change: + +```bash +yarn build:watch +``` + +### Clean + +```bash +yarn clean +``` + ### Lint ```bash diff --git a/packages/subproviders/CHANGELOG.json b/packages/subproviders/CHANGELOG.json index ca3889202..49d49cb7a 100644 --- a/packages/subproviders/CHANGELOG.json +++ b/packages/subproviders/CHANGELOG.json @@ -6,6 +6,10 @@ "note": "Refactor RedundantRPCSubprovider into RedundantSubprovider where it now accepts an array of subproviders rather then an array of RPC endpoints", "pr": 500 + }, + { + "note": "Add private key subprovider and refactor shared functionality into a base wallet subprovider", + "pr": 506 } ] }, diff --git a/packages/subproviders/README.md b/packages/subproviders/README.md index ac92b89d2..2936be545 100644 --- a/packages/subproviders/README.md +++ b/packages/subproviders/README.md @@ -22,11 +22,11 @@ If your project is in [TypeScript](https://www.typescriptlang.org/), add the fol ## Contributing -We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository. +We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository. Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. -### Install Dependencies +### Install dependencies If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them: @@ -34,17 +34,33 @@ If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them: yarn config set workspaces-experimental true ``` +Then install dependencies + ```bash yarn install ``` ### Build +If this is your **first** time building this package, you must first build **all** packages within the monorepo. This is because packages that depend on other packages located inside this monorepo are symlinked when run from **within** the monorepo. This allows you to make changes across multiple packages without first publishing dependent packages to NPM. To build all packages, run the following from the monorepo root directory: + +```bash +yarn lerna:rebuild +``` + +Or continuously rebuild on change: + +```bash +yarn dev +``` + +You can also build this specific package by running the following from within its directory: + ```bash yarn build ``` -or +or continuously rebuild on change: ```bash yarn build:watch diff --git a/packages/subproviders/src/index.ts b/packages/subproviders/src/index.ts index 1711387f1..b84473e45 100644 --- a/packages/subproviders/src/index.ts +++ b/packages/subproviders/src/index.ts @@ -12,6 +12,7 @@ export { LedgerSubprovider } from './subproviders/ledger'; export { GanacheSubprovider } from './subproviders/ganache'; export { Subprovider } from './subproviders/subprovider'; export { NonceTrackerSubprovider } from './subproviders/nonce_tracker'; +export { PrivateKeyWalletSubprovider } from './subproviders/private_key_wallet_subprovider'; export { Callback, ErrorCallback, diff --git a/packages/subproviders/src/subproviders/base_wallet_subprovider.ts b/packages/subproviders/src/subproviders/base_wallet_subprovider.ts new file mode 100644 index 000000000..034f83e7f --- /dev/null +++ b/packages/subproviders/src/subproviders/base_wallet_subprovider.ts @@ -0,0 +1,137 @@ +import { assert } from '@0xproject/assert'; +import { JSONRPCRequestPayload, JSONRPCResponsePayload } from '@0xproject/types'; +import { addressUtils } from '@0xproject/utils'; +import * as _ from 'lodash'; + +import { Callback, ErrorCallback, PartialTxParams, ResponseWithTxParams, WalletSubproviderErrors } from '../types'; + +import { Subprovider } from './subprovider'; + +export abstract class BaseWalletSubprovider extends Subprovider { + protected static _validateTxParams(txParams: PartialTxParams) { + assert.isETHAddressHex('to', txParams.to); + assert.isHexString('nonce', txParams.nonce); + assert.isHexString('gas', txParams.gas); + } + private static _validateSender(sender: string) { + if (_.isUndefined(sender) || !addressUtils.isAddress(sender)) { + throw new Error(WalletSubproviderErrors.SenderInvalidOrNotSupplied); + } + } + + public abstract async getAccountsAsync(): Promise<string[]>; + public abstract async signTransactionAsync(txParams: PartialTxParams): Promise<string>; + public abstract async signPersonalMessageAsync(data: string): Promise<string>; + + /** + * This method conforms to the web3-provider-engine interface. + * It is called internally by the ProviderEngine when it is this subproviders + * turn to handle a JSON RPC request. + * @param payload JSON RPC payload + * @param next Callback to call if this subprovider decides not to handle the request + * @param end Callback to call if subprovider handled the request and wants to pass back the request. + */ + // tslint:disable-next-line:async-suffix + public async handleRequest(payload: JSONRPCRequestPayload, next: Callback, end: ErrorCallback) { + let accounts; + let txParams; + switch (payload.method) { + case 'eth_coinbase': + try { + accounts = await this.getAccountsAsync(); + end(null, accounts[0]); + } catch (err) { + end(err); + } + return; + + case 'eth_accounts': + try { + accounts = await this.getAccountsAsync(); + end(null, accounts); + } catch (err) { + end(err); + } + return; + + case 'eth_sendTransaction': + txParams = payload.params[0]; + try { + BaseWalletSubprovider._validateSender(txParams.from); + const filledParams = await this._populateMissingTxParamsAsync(txParams); + const signedTx = await this.signTransactionAsync(filledParams); + const response = await this._emitSendTransactionAsync(signedTx); + end(null, response.result); + } catch (err) { + end(err); + } + return; + + case 'eth_signTransaction': + txParams = payload.params[0]; + try { + const filledParams = await this._populateMissingTxParamsAsync(txParams); + const signedTx = await this.signTransactionAsync(filledParams); + const result = { + raw: signedTx, + tx: txParams, + }; + end(null, result); + } catch (err) { + end(err); + } + return; + + case 'eth_sign': + case 'personal_sign': + const data = payload.method === 'eth_sign' ? payload.params[1] : payload.params[0]; + try { + const ecSignatureHex = await this.signPersonalMessageAsync(data); + end(null, ecSignatureHex); + } catch (err) { + end(err); + } + return; + + default: + next(); + return; + } + } + private async _emitSendTransactionAsync(signedTx: string): Promise<JSONRPCResponsePayload> { + const payload = { + method: 'eth_sendRawTransaction', + params: [signedTx], + }; + const result = await this.emitPayloadAsync(payload); + return result; + } + private async _populateMissingTxParamsAsync(partialTxParams: PartialTxParams): Promise<PartialTxParams> { + let txParams = partialTxParams; + if (_.isUndefined(partialTxParams.gasPrice)) { + const gasPriceResult = await this.emitPayloadAsync({ + method: 'eth_gasPrice', + params: [], + }); + const gasPrice = gasPriceResult.result.toString(); + txParams = { ...txParams, gasPrice }; + } + if (_.isUndefined(partialTxParams.nonce)) { + const nonceResult = await this.emitPayloadAsync({ + method: 'eth_getTransactionCount', + params: [partialTxParams.from, 'pending'], + }); + const nonce = nonceResult.result; + txParams = { ...txParams, nonce }; + } + if (_.isUndefined(partialTxParams.gas)) { + const gasResult = await this.emitPayloadAsync({ + method: 'eth_estimateGas', + params: [partialTxParams], + }); + const gas = gasResult.result.toString(); + txParams = { ...txParams, gas }; + } + return txParams; + } +} diff --git a/packages/subproviders/src/subproviders/ledger.ts b/packages/subproviders/src/subproviders/ledger.ts index 95784a391..aa86bf6c0 100644 --- a/packages/subproviders/src/subproviders/ledger.ts +++ b/packages/subproviders/src/subproviders/ledger.ts @@ -15,9 +15,10 @@ import { LedgerSubproviderErrors, PartialTxParams, ResponseWithTxParams, + WalletSubproviderErrors, } from '../types'; -import { Subprovider } from './subprovider'; +import { BaseWalletSubprovider } from './base_wallet_subprovider'; const DEFAULT_DERIVATION_PATH = `44'/60'/0'`; const DEFAULT_NUM_ADDRESSES_TO_FETCH = 10; @@ -29,7 +30,7 @@ const SHOULD_GET_CHAIN_CODE = true; * This subprovider intercepts all account related RPC requests (e.g message/transaction signing, etc...) and * re-routes them to a Ledger device plugged into the users computer. */ -export class LedgerSubprovider extends Subprovider { +export class LedgerSubprovider extends BaseWalletSubprovider { private _nonceLock = new Lock(); private _connectionLock = new Lock(); private _networkId: number; @@ -38,11 +39,6 @@ export class LedgerSubprovider extends Subprovider { private _ledgerEthereumClientFactoryAsync: LedgerEthereumClientFactoryAsync; private _ledgerClientIfExists?: LedgerEthereumClient; private _shouldAlwaysAskForConfirmation: boolean; - private static _validateSender(sender: string) { - if (_.isUndefined(sender) || !addressUtils.isAddress(sender)) { - throw new Error(LedgerSubproviderErrors.SenderInvalidOrNotSupplied); - } - } /** * Instantiates a LedgerSubprovider. Defaults to derivationPath set to `44'/60'/0'`. * TestRPC/Ganache defaults to `m/44'/60'/0'/0`, so set this in the configs if desired. @@ -133,6 +129,7 @@ export class LedgerSubprovider extends Subprovider { * @return Signed transaction hex string */ public async signTransactionAsync(txParams: PartialTxParams): Promise<string> { + LedgerSubprovider._validateTxParams(txParams); this._ledgerClientIfExists = await this._createLedgerClientAsync(); const tx = new EthereumTx(txParams); @@ -168,7 +165,7 @@ export class LedgerSubprovider extends Subprovider { } } /** - * Sign a personal Ethereum signed message. The signing address will be to one + * Sign a personal Ethereum signed message. The signing address will be the one * retrieved given a derivationPath and pathIndex set on the subprovider. * The Ledger adds the Ethereum signed message prefix on-device. If you've added * the LedgerSubprovider to your app's provider, you can simply send an `eth_sign` @@ -178,6 +175,10 @@ export class LedgerSubprovider extends Subprovider { * @return Signature hex string (order: rsv) */ public async signPersonalMessageAsync(data: string): Promise<string> { + if (_.isUndefined(data)) { + throw new Error(WalletSubproviderErrors.DataMissingForSignPersonalMessage); + } + assert.isHexString('data', data); this._ledgerClientIfExists = await this._createLedgerClientAsync(); try { const derivationPath = this._getDerivationPath(); @@ -198,82 +199,6 @@ export class LedgerSubprovider extends Subprovider { throw err; } } - /** - * This method conforms to the web3-provider-engine interface. - * It is called internally by the ProviderEngine when it is this subproviders - * turn to handle a JSON RPC request. - * @param payload JSON RPC payload - * @param next Callback to call if this subprovider decides not to handle the request - * @param end Callback to call if subprovider handled the request and wants to pass back the request. - */ - // tslint:disable-next-line:async-suffix - public async handleRequest( - payload: JSONRPCRequestPayload, - next: Callback, - end: (err: Error | null, result?: any) => void, - ) { - let accounts; - let txParams; - switch (payload.method) { - case 'eth_coinbase': - try { - accounts = await this.getAccountsAsync(); - end(null, accounts[0]); - } catch (err) { - end(err); - } - return; - - case 'eth_accounts': - try { - accounts = await this.getAccountsAsync(); - end(null, accounts); - } catch (err) { - end(err); - } - return; - - case 'eth_sendTransaction': - txParams = payload.params[0]; - try { - LedgerSubprovider._validateSender(txParams.from); - const result = await this._sendTransactionAsync(txParams); - end(null, result); - } catch (err) { - end(err); - } - return; - - case 'eth_signTransaction': - txParams = payload.params[0]; - try { - const result = await this._signTransactionWithoutSendingAsync(txParams); - end(null, result); - } catch (err) { - end(err); - } - return; - - case 'eth_sign': - case 'personal_sign': - const data = payload.method === 'eth_sign' ? payload.params[1] : payload.params[0]; - try { - if (_.isUndefined(data)) { - throw new Error(LedgerSubproviderErrors.DataMissingForSignPersonalMessage); - } - assert.isHexString('data', data); - const ecSignatureHex = await this.signPersonalMessageAsync(data); - end(null, ecSignatureHex); - } catch (err) { - end(err); - } - return; - - default: - next(); - return; - } - } private _getDerivationPath() { const derivationPath = `${this.getPath()}/${this._derivationPathIndex}`; return derivationPath; @@ -298,70 +223,4 @@ export class LedgerSubprovider extends Subprovider { this._ledgerClientIfExists = undefined; this._connectionLock.release(); } - private async _sendTransactionAsync(txParams: PartialTxParams): Promise<string> { - await this._nonceLock.acquire(); - try { - // fill in the extras - const filledParams = await this._populateMissingTxParamsAsync(txParams); - // sign it - const signedTx = await this.signTransactionAsync(filledParams); - // emit a submit - const payload = { - method: 'eth_sendRawTransaction', - params: [signedTx], - }; - const result = await this.emitPayloadAsync(payload); - this._nonceLock.release(); - return result.result; - } catch (err) { - this._nonceLock.release(); - throw err; - } - } - private async _signTransactionWithoutSendingAsync(txParams: PartialTxParams): Promise<ResponseWithTxParams> { - await this._nonceLock.acquire(); - try { - // fill in the extras - const filledParams = await this._populateMissingTxParamsAsync(txParams); - // sign it - const signedTx = await this.signTransactionAsync(filledParams); - - this._nonceLock.release(); - const result = { - raw: signedTx, - tx: txParams, - }; - return result; - } catch (err) { - this._nonceLock.release(); - throw err; - } - } - private async _populateMissingTxParamsAsync(txParams: PartialTxParams): Promise<PartialTxParams> { - if (_.isUndefined(txParams.gasPrice)) { - const gasPriceResult = await this.emitPayloadAsync({ - method: 'eth_gasPrice', - params: [], - }); - const gasPrice = gasPriceResult.result.toString(); - txParams.gasPrice = gasPrice; - } - if (_.isUndefined(txParams.nonce)) { - const nonceResult = await this.emitPayloadAsync({ - method: 'eth_getTransactionCount', - params: [txParams.from, 'pending'], - }); - const nonce = nonceResult.result; - txParams.nonce = nonce; - } - if (_.isUndefined(txParams.gas)) { - const gasResult = await this.emitPayloadAsync({ - method: 'eth_estimateGas', - params: [txParams], - }); - const gas = gasResult.result.toString(); - txParams.gas = gas; - } - return txParams; - } } diff --git a/packages/subproviders/src/subproviders/private_key_wallet_subprovider.ts b/packages/subproviders/src/subproviders/private_key_wallet_subprovider.ts new file mode 100644 index 000000000..c3a53773a --- /dev/null +++ b/packages/subproviders/src/subproviders/private_key_wallet_subprovider.ts @@ -0,0 +1,70 @@ +import { assert } from '@0xproject/assert'; +import { JSONRPCRequestPayload } from '@0xproject/types'; +import EthereumTx = require('ethereumjs-tx'); +import * as ethUtil from 'ethereumjs-util'; +import * as _ from 'lodash'; + +import { Callback, ErrorCallback, PartialTxParams, ResponseWithTxParams, WalletSubproviderErrors } from '../types'; + +import { BaseWalletSubprovider } from './base_wallet_subprovider'; +import { Subprovider } from './subprovider'; + +/** + * This class implements the [web3-provider-engine](https://github.com/MetaMask/provider-engine) subprovider interface. + * This subprovider intercepts all account related RPC requests (e.g message/transaction signing, etc...) and handles + * all requests with the supplied Ethereum private key. + */ +export class PrivateKeyWalletSubprovider extends BaseWalletSubprovider { + private _address: string; + private _privateKeyBuffer: Buffer; + constructor(privateKey: string) { + assert.isString('privateKey', privateKey); + super(); + this._privateKeyBuffer = new Buffer(privateKey, 'hex'); + this._address = `0x${ethUtil.privateToAddress(this._privateKeyBuffer).toString('hex')}`; + } + /** + * Retrieve the account associated with the supplied private key. + * This method is implicitly called when issuing a `eth_accounts` JSON RPC request + * via your providerEngine instance. + * @return An array of accounts + */ + public async getAccountsAsync(): Promise<string[]> { + return [this._address]; + } + /** + * Sign a transaction with the private key. If you've added this Subprovider to your + * app's provider, you can simply send an `eth_sendTransaction` JSON RPC request, and + * this method will be called auto-magically. If you are not using this via a ProviderEngine + * instance, you can call it directly. + * @param txParams Parameters of the transaction to sign + * @return Signed transaction hex string + */ + public async signTransactionAsync(txParams: PartialTxParams): Promise<string> { + PrivateKeyWalletSubprovider._validateTxParams(txParams); + const tx = new EthereumTx(txParams); + tx.sign(this._privateKeyBuffer); + const rawTx = `0x${tx.serialize().toString('hex')}`; + return rawTx; + } + /** + * Sign a personal Ethereum signed message. The signing address will be + * calculated from the private key. + * If you've added the PKWalletSubprovider to your app's provider, you can simply send an `eth_sign` + * or `personal_sign` JSON RPC request, and this method will be called auto-magically. + * If you are not using this via a ProviderEngine instance, you can call it directly. + * @param data Message to sign + * @return Signature hex string (order: rsv) + */ + public async signPersonalMessageAsync(dataIfExists: string): Promise<string> { + if (_.isUndefined(dataIfExists)) { + throw new Error(WalletSubproviderErrors.DataMissingForSignPersonalMessage); + } + assert.isHexString('data', dataIfExists); + const dataBuff = ethUtil.toBuffer(dataIfExists); + const msgHashBuff = ethUtil.hashPersonalMessage(dataBuff); + const sig = ethUtil.ecsign(msgHashBuff, this._privateKeyBuffer); + const rpcSig = ethUtil.toRpcSig(sig.v, sig.r, sig.s); + return rpcSig; + } +} diff --git a/packages/subproviders/src/types.ts b/packages/subproviders/src/types.ts index a1fec1882..bacb7091b 100644 --- a/packages/subproviders/src/types.ts +++ b/packages/subproviders/src/types.ts @@ -95,11 +95,13 @@ export interface ResponseWithTxParams { tx: PartialTxParams; } +export enum WalletSubproviderErrors { + DataMissingForSignPersonalMessage = 'DATA_MISSING_FOR_SIGN_PERSONAL_MESSAGE', + SenderInvalidOrNotSupplied = 'SENDER_INVALID_OR_NOT_SUPPLIED', +} export enum LedgerSubproviderErrors { TooOldLedgerFirmware = 'TOO_OLD_LEDGER_FIRMWARE', FromAddressMissingOrInvalid = 'FROM_ADDRESS_MISSING_OR_INVALID', - DataMissingForSignPersonalMessage = 'DATA_MISSING_FOR_SIGN_PERSONAL_MESSAGE', - SenderInvalidOrNotSupplied = 'SENDER_INVALID_OR_NOT_SUPPLIED', MultipleOpenConnectionsDisallowed = 'MULTIPLE_OPEN_CONNECTIONS_DISALLOWED', } diff --git a/packages/subproviders/test/integration/ledger_subprovider_test.ts b/packages/subproviders/test/integration/ledger_subprovider_test.ts index bc60b4330..503618089 100644 --- a/packages/subproviders/test/integration/ledger_subprovider_test.ts +++ b/packages/subproviders/test/integration/ledger_subprovider_test.ts @@ -14,6 +14,7 @@ import RpcSubprovider = require('web3-provider-engine/subproviders/rpc'); import { LedgerSubprovider } from '../../src'; import { DoneCallback, LedgerEthereumClient } from '../../src/types'; import { chaiSetup } from '../chai_setup'; +import { fixtureData } from '../utils/fixture_data'; import { reportCallbackErrors } from '../utils/report_callback_errors'; chaiSetup.configure(); @@ -25,17 +26,14 @@ async function ledgerEthereumNodeJsClientFactoryAsync(): Promise<LedgerEthereumC return ledgerEthClient; } -const TESTRPC_DERIVATION_PATH = `m/44'/60'/0'/0`; -const TEST_RPC_ACCOUNT_0 = '0x5409ed021d9299bf6814279a6a1411a7e866a631'; - describe('LedgerSubprovider', () => { let ledgerSubprovider: LedgerSubprovider; - const networkId: number = 42; + const networkId: number = fixtureData.NETWORK_ID; before(async () => { ledgerSubprovider = new LedgerSubprovider({ networkId, ledgerEthereumClientFactoryAsync: ledgerEthereumNodeJsClientFactoryAsync, - derivationPath: TESTRPC_DERIVATION_PATH, + derivationPath: fixtureData.TESTRPC_DERIVATION_PATH, }); }); describe('direct method calls', () => { @@ -46,7 +44,7 @@ describe('LedgerSubprovider', () => { }); it('returns the expected first account from a ledger set up with the test mnemonic', async () => { const accounts = await ledgerSubprovider.getAccountsAsync(); - expect(accounts[0]).to.be.equal(TEST_RPC_ACCOUNT_0); + expect(accounts[0]).to.be.equal(fixtureData.TEST_RPC_ACCOUNT_0); }); it('returns requested number of accounts', async () => { const numberOfAccounts = 20; @@ -55,24 +53,14 @@ describe('LedgerSubprovider', () => { expect(accounts.length).to.be.equal(numberOfAccounts); }); it('signs a personal message', async () => { - const data = ethUtils.bufferToHex(ethUtils.toBuffer('hello world')); + const data = ethUtils.bufferToHex(ethUtils.toBuffer(fixtureData.PERSONAL_MESSAGE_STRING)); const ecSignatureHex = await ledgerSubprovider.signPersonalMessageAsync(data); expect(ecSignatureHex.length).to.be.equal(132); - expect(ecSignatureHex.substr(0, 2)).to.be.equal('0x'); + expect(ecSignatureHex).to.be.equal(fixtureData.PERSONAL_MESSAGE_SIGNED_RESULT); }); it('signs a transaction', async () => { - const tx = { - nonce: '0x00', - gas: '0x2710', - to: '0x0000000000000000000000000000000000000000', - value: '0x00', - chainId: 3, - from: TEST_RPC_ACCOUNT_0, - }; - const txHex = await ledgerSubprovider.signTransactionAsync(tx); - expect(txHex).to.be.equal( - '0xf85f8080822710940000000000000000000000000000000000000000808078a0712854c73c69445cc1b22a7c3d7312ff9a97fe4ffba35fd636e8236b211b6e7ca0647cee031615e52d916c7c707025bc64ad525d8f1b9876c3435a863b42743178', - ); + const txHex = await ledgerSubprovider.signTransactionAsync(fixtureData.TX_DATA); + expect(txHex).to.be.equal(fixtureData.TX_DATA_SIGNED_RESULT); }); }); describe('calls through a provider', () => { @@ -146,20 +134,15 @@ describe('LedgerSubprovider', () => { })().catch(done); }); it('signs a transaction', (done: DoneCallback) => { - const tx = { - to: '0xafa3f8684e54059998bc3a7b0d2b0da075154d66', - value: '0x00', - }; const payload = { jsonrpc: '2.0', method: 'eth_signTransaction', - params: [tx], + params: [fixtureData.TX_DATA], id: 1, }; const callback = reportCallbackErrors(done)((err: Error, response: JSONRPCResponsePayload) => { expect(err).to.be.a('null'); - expect(response.result.raw.length).to.be.equal(206); - expect(response.result.raw.substr(0, 2)).to.be.equal('0x'); + expect(response.result.raw).to.be.equal(fixtureData.TX_DATA_SIGNED_RESULT); done(); }); ledgerProvider.sendAsync(payload, callback); @@ -171,7 +154,7 @@ describe('LedgerSubprovider', () => { // Give first account on Ledger sufficient ETH to complete tx send let tx = { to: accounts[0], - from: TEST_RPC_ACCOUNT_0, + from: fixtureData.TEST_RPC_ACCOUNT_0, value: '0x8ac7230489e80000', // 10 ETH }; let payload = { diff --git a/packages/subproviders/test/unit/ledger_subprovider_test.ts b/packages/subproviders/test/unit/ledger_subprovider_test.ts index d30c68f65..88d38f59b 100644 --- a/packages/subproviders/test/unit/ledger_subprovider_test.ts +++ b/packages/subproviders/test/unit/ledger_subprovider_test.ts @@ -6,8 +6,14 @@ import Web3 = require('web3'); import Web3ProviderEngine = require('web3-provider-engine'); import { LedgerSubprovider } from '../../src'; -import { DoneCallback, LedgerCommunicationClient, LedgerSubproviderErrors } from '../../src/types'; +import { + DoneCallback, + LedgerCommunicationClient, + LedgerSubproviderErrors, + WalletSubproviderErrors, +} from '../../src/types'; import { chaiSetup } from '../chai_setup'; +import { fixtureData } from '../utils/fixture_data'; import { ganacheSubprovider } from '../utils/ganache_subprovider'; import { reportCallbackErrors } from '../utils/report_callback_errors'; @@ -75,7 +81,7 @@ describe('LedgerSubprovider', () => { expect(accounts.length).to.be.equal(numberOfAccounts); }); it('signs a personal message', async () => { - const data = ethUtils.bufferToHex(ethUtils.toBuffer('hello world')); + const data = ethUtils.bufferToHex(ethUtils.toBuffer(fixtureData.PERSONAL_MESSAGE_STRING)); const ecSignatureHex = await ledgerSubprovider.signPersonalMessageAsync(data); expect(ecSignatureHex).to.be.equal( '0xa6cc284bff14b42bdf5e9286730c152be91719d478605ec46b3bebcd0ae491480652a1a7b742ceb0213d1e744316e285f41f878d8af0b8e632cbca4c279132d001', @@ -136,7 +142,7 @@ describe('LedgerSubprovider', () => { provider.sendAsync(payload, callback); }); it('signs a personal message with personal_sign', (done: DoneCallback) => { - const messageHex = ethUtils.bufferToHex(ethUtils.toBuffer('hello world')); + const messageHex = ethUtils.bufferToHex(ethUtils.toBuffer(fixtureData.PERSONAL_MESSAGE_STRING)); const payload = { jsonrpc: '2.0', method: 'personal_sign', @@ -219,7 +225,7 @@ describe('LedgerSubprovider', () => { }; const callback = reportCallbackErrors(done)((err: Error, response: JSONRPCResponsePayload) => { expect(err).to.not.be.a('null'); - expect(err.message).to.be.equal(LedgerSubproviderErrors.SenderInvalidOrNotSupplied); + expect(err.message).to.be.equal(WalletSubproviderErrors.SenderInvalidOrNotSupplied); done(); }); provider.sendAsync(payload, callback); @@ -238,7 +244,7 @@ describe('LedgerSubprovider', () => { }; const callback = reportCallbackErrors(done)((err: Error, response: JSONRPCResponsePayload) => { expect(err).to.not.be.a('null'); - expect(err.message).to.be.equal(LedgerSubproviderErrors.SenderInvalidOrNotSupplied); + expect(err.message).to.be.equal(WalletSubproviderErrors.SenderInvalidOrNotSupplied); done(); }); provider.sendAsync(payload, callback); diff --git a/packages/subproviders/test/unit/private_key_wallet_subprovider_test.ts b/packages/subproviders/test/unit/private_key_wallet_subprovider_test.ts new file mode 100644 index 000000000..ca0665871 --- /dev/null +++ b/packages/subproviders/test/unit/private_key_wallet_subprovider_test.ts @@ -0,0 +1,170 @@ +import { JSONRPCResponsePayload } from '@0xproject/types'; +import * as chai from 'chai'; +import * as ethUtils from 'ethereumjs-util'; +import * as _ from 'lodash'; +import Web3ProviderEngine = require('web3-provider-engine'); + +import { GanacheSubprovider, PrivateKeyWalletSubprovider } from '../../src/'; +import { + DoneCallback, + LedgerCommunicationClient, + LedgerSubproviderErrors, + WalletSubproviderErrors, +} from '../../src/types'; +import { chaiSetup } from '../chai_setup'; +import { fixtureData } from '../utils/fixture_data'; +import { reportCallbackErrors } from '../utils/report_callback_errors'; + +chaiSetup.configure(); +const expect = chai.expect; + +describe('PrivateKeyWalletSubprovider', () => { + let subprovider: PrivateKeyWalletSubprovider; + before(async () => { + subprovider = new PrivateKeyWalletSubprovider(fixtureData.TEST_RPC_ACCOUNT_0_ACCOUNT_PRIVATE_KEY); + }); + describe('direct method calls', () => { + describe('success cases', () => { + it('returns the account', async () => { + const accounts = await subprovider.getAccountsAsync(); + expect(accounts[0]).to.be.equal(fixtureData.TEST_RPC_ACCOUNT_0); + expect(accounts.length).to.be.equal(1); + }); + it('signs a personal message', async () => { + const data = ethUtils.bufferToHex(ethUtils.toBuffer(fixtureData.PERSONAL_MESSAGE_STRING)); + const ecSignatureHex = await subprovider.signPersonalMessageAsync(data); + expect(ecSignatureHex).to.be.equal(fixtureData.PERSONAL_MESSAGE_SIGNED_RESULT); + }); + it('signs a transaction', async () => { + const txHex = await subprovider.signTransactionAsync(fixtureData.TX_DATA); + expect(txHex).to.be.equal(fixtureData.TX_DATA_SIGNED_RESULT); + }); + }); + }); + describe('calls through a provider', () => { + let provider: Web3ProviderEngine; + before(() => { + provider = new Web3ProviderEngine(); + provider.addProvider(subprovider); + const ganacheSubprovider = new GanacheSubprovider({}); + provider.addProvider(ganacheSubprovider); + provider.start(); + }); + describe('success cases', () => { + it('returns a list of accounts', (done: DoneCallback) => { + const payload = { + jsonrpc: '2.0', + method: 'eth_accounts', + params: [], + id: 1, + }; + const callback = reportCallbackErrors(done)((err: Error, response: JSONRPCResponsePayload) => { + expect(err).to.be.a('null'); + expect(response.result[0]).to.be.equal(fixtureData.TEST_RPC_ACCOUNT_0); + expect(response.result.length).to.be.equal(1); + done(); + }); + provider.sendAsync(payload, callback); + }); + it('signs a personal message with eth_sign', (done: DoneCallback) => { + const messageHex = ethUtils.bufferToHex(ethUtils.toBuffer(fixtureData.PERSONAL_MESSAGE_STRING)); + const payload = { + jsonrpc: '2.0', + method: 'eth_sign', + params: ['0x0000000000000000000000000000000000000000', messageHex], + id: 1, + }; + const callback = reportCallbackErrors(done)((err: Error, response: JSONRPCResponsePayload) => { + expect(err).to.be.a('null'); + expect(response.result).to.be.equal(fixtureData.PERSONAL_MESSAGE_SIGNED_RESULT); + done(); + }); + provider.sendAsync(payload, callback); + }); + it('signs a personal message with personal_sign', (done: DoneCallback) => { + const messageHex = ethUtils.bufferToHex(ethUtils.toBuffer(fixtureData.PERSONAL_MESSAGE_STRING)); + const payload = { + jsonrpc: '2.0', + method: 'personal_sign', + params: [messageHex, '0x0000000000000000000000000000000000000000'], + id: 1, + }; + const callback = reportCallbackErrors(done)((err: Error, response: JSONRPCResponsePayload) => { + expect(err).to.be.a('null'); + expect(response.result).to.be.equal(fixtureData.PERSONAL_MESSAGE_SIGNED_RESULT); + done(); + }); + provider.sendAsync(payload, callback); + }); + }); + describe('failure cases', () => { + it('should throw if `data` param not hex when calling eth_sign', (done: DoneCallback) => { + const nonHexMessage = 'hello world'; + const payload = { + jsonrpc: '2.0', + method: 'eth_sign', + params: ['0x0000000000000000000000000000000000000000', nonHexMessage], + id: 1, + }; + const callback = reportCallbackErrors(done)((err: Error, response: JSONRPCResponsePayload) => { + expect(err).to.not.be.a('null'); + expect(err.message).to.be.equal('Expected data to be of type HexString, encountered: hello world'); + done(); + }); + provider.sendAsync(payload, callback); + }); + it('should throw if `data` param not hex when calling personal_sign', (done: DoneCallback) => { + const nonHexMessage = 'hello world'; + const payload = { + jsonrpc: '2.0', + method: 'personal_sign', + params: [nonHexMessage, '0x0000000000000000000000000000000000000000'], + id: 1, + }; + const callback = reportCallbackErrors(done)((err: Error, response: JSONRPCResponsePayload) => { + expect(err).to.not.be.a('null'); + expect(err.message).to.be.equal('Expected data to be of type HexString, encountered: hello world'); + done(); + }); + provider.sendAsync(payload, callback); + }); + it('should throw if `from` param missing when calling eth_sendTransaction', (done: DoneCallback) => { + const tx = { + to: '0xafa3f8684e54059998bc3a7b0d2b0da075154d66', + value: '0xde0b6b3a7640000', + }; + const payload = { + jsonrpc: '2.0', + method: 'eth_sendTransaction', + params: [tx], + id: 1, + }; + const callback = reportCallbackErrors(done)((err: Error, response: JSONRPCResponsePayload) => { + expect(err).to.not.be.a('null'); + expect(err.message).to.be.equal(WalletSubproviderErrors.SenderInvalidOrNotSupplied); + done(); + }); + provider.sendAsync(payload, callback); + }); + it('should throw if `from` param invalid address when calling eth_sendTransaction', (done: DoneCallback) => { + const tx = { + to: '0xafa3f8684e54059998bc3a7b0d2b0da075154d66', + from: '0xIncorrectEthereumAddress', + value: '0xde0b6b3a7640000', + }; + const payload = { + jsonrpc: '2.0', + method: 'eth_sendTransaction', + params: [tx], + id: 1, + }; + const callback = reportCallbackErrors(done)((err: Error, response: JSONRPCResponsePayload) => { + expect(err).to.not.be.a('null'); + expect(err.message).to.be.equal(WalletSubproviderErrors.SenderInvalidOrNotSupplied); + done(); + }); + provider.sendAsync(payload, callback); + }); + }); + }); +}); diff --git a/packages/subproviders/test/utils/fixture_data.ts b/packages/subproviders/test/utils/fixture_data.ts new file mode 100644 index 000000000..890573d0d --- /dev/null +++ b/packages/subproviders/test/utils/fixture_data.ts @@ -0,0 +1,23 @@ +const TEST_RPC_ACCOUNT_0 = '0x5409ed021d9299bf6814279a6a1411a7e866a631'; +const networkId = 42; +export const fixtureData = { + TEST_RPC_ACCOUNT_0, + TEST_RPC_ACCOUNT_0_ACCOUNT_PRIVATE_KEY: 'F2F48EE19680706196E2E339E5DA3491186E0C4C5030670656B0E0164837257D', + PERSONAL_MESSAGE_STRING: 'hello world', + PERSONAL_MESSAGE_SIGNED_RESULT: + '0x1b0ec5e2908e993d0c8ab6b46da46be2688fdf03c7ea6686075de37392e50a7d7fcc531446699132fbda915bd989882e0064d417018773a315fb8d43ed063c9b00', + TESTRPC_DERIVATION_PATH: `m/44'/60'/0'/0`, + NETWORK_ID: networkId, + TX_DATA: { + nonce: '0x00', + gasPrice: '0x0', + gas: '0x2710', + to: '0x0000000000000000000000000000000000000000', + value: '0x00', + chainId: networkId, + from: TEST_RPC_ACCOUNT_0, + }, + // This is the signed result of the abouve Transaction Data + TX_DATA_SIGNED_RESULT: + '0xf85f8080822710940000000000000000000000000000000000000000808078a0712854c73c69445cc1b22a7c3d7312ff9a97fe4ffba35fd636e8236b211b6e7ca0647cee031615e52d916c7c707025bc64ad525d8f1b9876c3435a863b42743178', +}; diff --git a/packages/testnet-faucets/README.md b/packages/testnet-faucets/README.md index 2f6ae347f..93f97e1b7 100644 --- a/packages/testnet-faucets/README.md +++ b/packages/testnet-faucets/README.md @@ -4,15 +4,15 @@ This faucet dispenses 0.1 test ether to one recipient per second and 0.1 test ZR ## Installation -This is a private package and therefore is not published to npm. In order to build and run this package locally, see the [Install Dependencies](#Install-Dependencies) section and onwards below. +This is a private package and therefore is not published to npm. In order to build and run this package locally, see the contributing instructions below. ## Contributing -We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository. +We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository. Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. -### Install Dependencies +### Install dependencies If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them: @@ -26,6 +26,44 @@ Then install dependencies yarn install ``` +### Build + +If this is your **first** time building this package, you must first build **all** packages within the monorepo. This is because packages that depend on other packages located inside this monorepo are symlinked when run from **within** the monorepo. This allows you to make changes across multiple packages without first publishing dependent packages to NPM. To build all packages, run the following from the monorepo root directory: + +```bash +yarn lerna:rebuild +``` + +Or continuously rebuild on change: + +```bash +yarn dev +``` + +You can also build this specific package by running the following from within its directory: + +```bash +yarn build +``` + +or continuously rebuild on change: + +```bash +yarn build:watch +``` + +### Clean + +```bash +yarn clean +``` + +### Lint + +```bash +yarn lint +``` + ### Start Set the following environment variables: @@ -125,9 +163,3 @@ docker run -d \ -e INFURA_API_KEY=$INFURA_API_KEY \ testnet-faucets ``` - -### Lint - -```bash -yarn lint -``` diff --git a/packages/testnet-faucets/src/ts/handler.ts b/packages/testnet-faucets/src/ts/handler.ts index f9ac484de..a6e786552 100644 --- a/packages/testnet-faucets/src/ts/handler.ts +++ b/packages/testnet-faucets/src/ts/handler.ts @@ -9,15 +9,13 @@ import * as Web3 from 'web3'; // we are not running in a browser env. // Filed issue: https://github.com/ethereum/web3.js/issues/844 (global as any).XMLHttpRequest = undefined; -import { NonceTrackerSubprovider } from '@0xproject/subproviders'; +import { NonceTrackerSubprovider, PrivateKeyWalletSubprovider } from '@0xproject/subproviders'; import ProviderEngine = require('web3-provider-engine'); -import HookedWalletSubprovider = require('web3-provider-engine/subproviders/hooked-wallet'); import RpcSubprovider = require('web3-provider-engine/subproviders/rpc'); import { configs } from './configs'; import { DispatchQueue } from './dispatch_queue'; import { dispenseAssetTasks } from './dispense_asset_tasks'; -import { idManagement } from './id_management'; import { rpcUrls } from './rpc_urls'; interface NetworkConfig { @@ -41,9 +39,12 @@ const FIVE_DAYS_IN_MS = 4.32e8; // TODO: make this configurable export class Handler { private _networkConfigByNetworkId: ItemByNetworkId<NetworkConfig> = {}; private static _createProviderEngine(rpcUrl: string) { + if (_.isUndefined(configs.DISPENSER_PRIVATE_KEY)) { + throw new Error('Dispenser Private key not found'); + } const engine = new ProviderEngine(); engine.addProvider(new NonceTrackerSubprovider()); - engine.addProvider(new HookedWalletSubprovider(idManagement)); + engine.addProvider(new PrivateKeyWalletSubprovider(configs.DISPENSER_PRIVATE_KEY)); engine.addProvider( new RpcSubprovider({ rpcUrl, diff --git a/packages/testnet-faucets/src/ts/id_management.ts b/packages/testnet-faucets/src/ts/id_management.ts deleted file mode 100644 index 7c598f91c..000000000 --- a/packages/testnet-faucets/src/ts/id_management.ts +++ /dev/null @@ -1,35 +0,0 @@ -import EthereumTx = require('ethereumjs-tx'); -import * as ethUtil from 'ethereumjs-util'; -import * as _ from 'lodash'; - -import { configs } from './configs'; - -type Callback = (err: Error | null, result: any) => void; - -export const idManagement = { - getAccounts(callback: Callback) { - callback(null, [configs.DISPENSER_ADDRESS]); - }, - approveTransaction(txData: object, callback: Callback) { - callback(null, true); - }, - signTransaction(txData: object, callback: Callback) { - const tx = new EthereumTx(txData); - const privateKeyBuffer = new Buffer(configs.DISPENSER_PRIVATE_KEY as string, 'hex'); - tx.sign(privateKeyBuffer); - const rawTx = `0x${tx.serialize().toString('hex')}`; - callback(null, rawTx); - }, - signMessage(message: object, callback: Callback) { - const dataIfExists = _.get(message, 'data'); - if (_.isUndefined(dataIfExists)) { - callback(new Error('NO_DATA_TO_SIGN'), null); - } - const privateKeyBuffer = new Buffer(configs.DISPENSER_PRIVATE_KEY as string, 'hex'); - const dataBuff = ethUtil.toBuffer(dataIfExists); - const msgHashBuff = ethUtil.hashPersonalMessage(dataBuff); - const sig = ethUtil.ecsign(msgHashBuff, privateKeyBuffer); - const rpcSig = ethUtil.toRpcSig(sig.v, sig.r, sig.s); - callback(null, rpcSig); - }, -}; diff --git a/packages/tslint-config/README.md b/packages/tslint-config/README.md index b517f2d95..d90b3cf73 100644 --- a/packages/tslint-config/README.md +++ b/packages/tslint-config/README.md @@ -20,11 +20,11 @@ Add the following to your `tslint.json` file ## Contributing -We strongly encourage that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository. +We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository. Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. -### Install Dependencies +### Install dependencies If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them: @@ -40,16 +40,36 @@ yarn install ### Build +If this is your **first** time building this package, you must first build **all** packages within the monorepo. This is because packages that depend on other packages located inside this monorepo are symlinked when run from **within** the monorepo. This allows you to make changes across multiple packages without first publishing dependent packages to NPM. To build all packages, run the following from the monorepo root directory: + +```bash +yarn lerna:rebuild +``` + +Or continuously rebuild on change: + +```bash +yarn dev +``` + +You can also build this specific package by running the following from within its directory: + ```bash yarn build ``` -or +or continuously rebuild on change: ```bash yarn build:watch ``` +### Clean + +```bash +yarn clean +``` + ### Lint ```bash diff --git a/packages/types/README.md b/packages/types/README.md index b971eaee7..665701ea4 100644 --- a/packages/types/README.md +++ b/packages/types/README.md @@ -24,13 +24,13 @@ import { TransactionReceipt, TxData, TxDataPayable } from '@0xproject/types'; ## Contributing -We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository. +We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository. Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. -### Install Dependencies +### Install dependencies -If you don't have yarn workspaces e`nabled (Yarn < v1.0) - enable them: +If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them: ```bash yarn config set workspaces-experimental true @@ -44,16 +44,36 @@ yarn install ### Build +If this is your **first** time building this package, you must first build **all** packages within the monorepo. This is because packages that depend on other packages located inside this monorepo are symlinked when run from **within** the monorepo. This allows you to make changes across multiple packages without first publishing dependent packages to NPM. To build all packages, run the following from the monorepo root directory: + +```bash +yarn lerna:rebuild +``` + +Or continuously rebuild on change: + +```bash +yarn dev +``` + +You can also build this specific package by running the following from within its directory: + ```bash yarn build ``` -or +or continuously rebuild on change: ```bash yarn build:watch ``` +### Clean + +```bash +yarn clean +``` + ### Lint ```bash diff --git a/packages/typescript-typings/README.md b/packages/typescript-typings/README.md index 9a74739f3..968b2e87c 100644 --- a/packages/typescript-typings/README.md +++ b/packages/typescript-typings/README.md @@ -20,11 +20,11 @@ This will allow the TS compiler to first look into that repo and then fallback t ## Contributing -We strongly encourage that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository. +We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository. Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. -### Install Dependencies +### Install dependencies If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them: @@ -38,6 +38,38 @@ Then install dependencies yarn install ``` +### Build + +If this is your **first** time building this package, you must first build **all** packages within the monorepo. This is because packages that depend on other packages located inside this monorepo are symlinked when run from **within** the monorepo. This allows you to make changes across multiple packages without first publishing dependent packages to NPM. To build all packages, run the following from the monorepo root directory: + +```bash +yarn lerna:rebuild +``` + +Or continuously rebuild on change: + +```bash +yarn dev +``` + +You can also build this specific package by running the following from within its directory: + +```bash +yarn build +``` + +or continuously rebuild on change: + +```bash +yarn build:watch +``` + +### Clean + +```bash +yarn clean +``` + ### Lint ```bash diff --git a/packages/utils/README.md b/packages/utils/README.md index fde0780a9..914404937 100644 --- a/packages/utils/README.md +++ b/packages/utils/README.md @@ -24,11 +24,11 @@ import { addressUtils, bigNumberConfigs, classUtils, intervalUtils, promisify } ## Contributing -We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository. +We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository. Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. -### Install Dependencies +### Install dependencies If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them: @@ -44,16 +44,36 @@ yarn install ### Build +If this is your **first** time building this package, you must first build **all** packages within the monorepo. This is because packages that depend on other packages located inside this monorepo are symlinked when run from **within** the monorepo. This allows you to make changes across multiple packages without first publishing dependent packages to NPM. To build all packages, run the following from the monorepo root directory: + +```bash +yarn lerna:rebuild +``` + +Or continuously rebuild on change: + +```bash +yarn dev +``` + +You can also build this specific package by running the following from within its directory: + ```bash yarn build ``` -or +or continuously rebuild on change: ```bash yarn build:watch ``` +### Clean + +```bash +yarn clean +``` + ### Lint ```bash diff --git a/packages/web3-wrapper/README.md b/packages/web3-wrapper/README.md index e7491acb0..1ff85420a 100644 --- a/packages/web3-wrapper/README.md +++ b/packages/web3-wrapper/README.md @@ -20,11 +20,11 @@ If your project is in [TypeScript](https://www.typescriptlang.org/), add the fol ## Contributing -We strongly encourage that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository. +We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository. Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. -### Install Dependencies +### Install dependencies If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them: @@ -40,16 +40,36 @@ yarn install ### Build +If this is your **first** time building this package, you must first build **all** packages within the monorepo. This is because packages that depend on other packages located inside this monorepo are symlinked when run from **within** the monorepo. This allows you to make changes across multiple packages without first publishing dependent packages to NPM. To build all packages, run the following from the monorepo root directory: + +```bash +yarn lerna:rebuild +``` + +Or continuously rebuild on change: + +```bash +yarn dev +``` + +You can also build this specific package by running the following from within its directory: + ```bash yarn build ``` -or +or continuously rebuild on change: ```bash yarn build:watch ``` +### Clean + +```bash +yarn clean +``` + ### Lint ```bash diff --git a/packages/website/README.md b/packages/website/README.md index 042df52de..d93d18935 100644 --- a/packages/website/README.md +++ b/packages/website/README.md @@ -28,20 +28,24 @@ Add the following to your `/etc/hosts` file: yarn install ``` -### Run dev server +### Initial setup + +The **first** time you work with this package, you must build **all** packages within the monorepo. This is because packages that depend on other packages located inside this monorepo are symlinked when run from **within** the monorepo. This allows you to make changes across multiple packages without first publishing dependent packages to NPM. To build all packages, run the following from the monorepo root directory: ```bash -yarn run dev +yarn lerna:rebuild ``` -Visit [0xproject.localhost:3572](http://0xproject.localhost:3572) in your browser. +### Run dev server -### Build +The the `website` root directory, run: ```bash -yarn build +yarn dev ``` +Visit [0xproject.localhost:3572](http://0xproject.localhost:3572) in your browser. + ### Clean ```bash diff --git a/packages/website/public/images/landing/project_logos/ercdex.png b/packages/website/public/images/landing/project_logos/ercdex.png Binary files differindex 1367837e0..31d137be7 100644 --- a/packages/website/public/images/landing/project_logos/ercdex.png +++ b/packages/website/public/images/landing/project_logos/ercdex.png diff --git a/packages/website/ts/components/portal.tsx b/packages/website/ts/components/portal.tsx index ceb0ecc72..b79f5e288 100644 --- a/packages/website/ts/components/portal.tsx +++ b/packages/website/ts/components/portal.tsx @@ -15,6 +15,7 @@ import { EthWrappers } from 'ts/components/eth_wrappers'; import { FillOrder } from 'ts/components/fill_order'; import { Footer } from 'ts/components/footer'; import { PortalMenu } from 'ts/components/portal_menu'; +import { RelayerIndex } from 'ts/components/relayer_index/relayer_index'; import { TokenBalances } from 'ts/components/token_balances'; import { TopBar } from 'ts/components/top_bar/top_bar'; import { TradeHistory } from 'ts/components/trade_history/trade_history'; @@ -155,6 +156,7 @@ export class Portal extends React.Component<PortalAllProps, PortalAllState> { const updateShouldBlockchainErrDialogBeOpen = this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen.bind( this.props.dispatcher, ); + const isDevelopment = configs.ENVIRONMENT === Environments.DEVELOPMENT; const portalStyle: React.CSSProperties = { minHeight: '100vh', display: 'flex', @@ -204,12 +206,18 @@ export class Portal extends React.Component<PortalAllProps, PortalAllState> { <div className="py2" style={{ backgroundColor: colors.grey50 }}> {this.props.blockchainIsLoaded ? ( <Switch> - {configs.ENVIRONMENT === Environments.DEVELOPMENT && ( + {isDevelopment && ( <Route path={`${WebsitePaths.Portal}/wallet`} render={this._renderWallet.bind(this)} /> )} + {isDevelopment && ( + <Route + path={`${WebsitePaths.Portal}/relayers`} + render={this._renderRelayers.bind(this)} + /> + )} <Route path={`${WebsitePaths.Portal}/weth`} render={this._renderEthWrapper.bind(this)} @@ -313,6 +321,15 @@ export class Portal extends React.Component<PortalAllProps, PortalAllState> { </div> ); } + private _renderRelayers() { + return ( + <div className="flex flex-center"> + <div className="mx-auto" style={{ width: 800 }}> + <RelayerIndex networkId={this.props.networkId} /> + </div> + </div> + ); + } private _renderEthWrapper() { return ( <EthWrappers diff --git a/packages/website/ts/components/portal_menu.tsx b/packages/website/ts/components/portal_menu.tsx index 9ab611556..2b4d7eea2 100644 --- a/packages/website/ts/components/portal_menu.tsx +++ b/packages/website/ts/components/portal_menu.tsx @@ -59,14 +59,24 @@ export class PortalMenu extends React.Component<PortalMenuProps, PortalMenuState {this._renderMenuItemWithIcon('Wrap ETH', 'zmdi-circle-o')} </MenuItem> {configs.ENVIRONMENT === Environments.DEVELOPMENT && ( - <MenuItem - style={this.props.menuItemStyle} - className="py2" - to={`${WebsitePaths.Portal}/wallet`} - onClick={this.props.onClick.bind(this)} - > - {this._renderMenuItemWithIcon('Wallet', 'zmdi-balance-wallet')} - </MenuItem> + <div> + <MenuItem + style={this.props.menuItemStyle} + className="py2" + to={`${WebsitePaths.Portal}/wallet`} + onClick={this.props.onClick.bind(this)} + > + {this._renderMenuItemWithIcon('Wallet', 'zmdi-balance-wallet')} + </MenuItem> + <MenuItem + style={this.props.menuItemStyle} + className="py2" + to={`${WebsitePaths.Portal}/relayers`} + onClick={this.props.onClick.bind(this)} + > + {this._renderMenuItemWithIcon('Relayers', 'zmdi-input-antenna')} + </MenuItem> + </div> )} </div> ); diff --git a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx new file mode 100644 index 000000000..530576828 --- /dev/null +++ b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx @@ -0,0 +1,84 @@ +import { colors, Styles } from '@0xproject/react-shared'; +import * as _ from 'lodash'; +import { GridTile } from 'material-ui/GridList'; +import * as React from 'react'; + +import { TopTokens } from 'ts/components/relayer_index/relayer_top_tokens'; +import { TokenIcon } from 'ts/components/ui/token_icon'; +import { RelayerInfo, Token } from 'ts/types'; + +export interface RelayerGridTileProps { + relayerInfo: RelayerInfo; + networkId: number; +} + +const styles: Styles = { + root: { + backgroundColor: colors.white, + borderBottomRightRadius: 10, + borderBottomLeftRadius: 10, + borderTopRightRadius: 10, + borderTopLeftRadius: 10, + boxShadow: `0px 4px 6px ${colors.walletBoxShadow}`, + overflow: 'hidden', + boxSizing: 'border-box', + }, + innerDiv: { + padding: 6, + height: '100%', + boxSizing: 'border-box', + }, + header: { + height: '50%', + width: '100%', + objectFit: 'cover', + borderBottomRightRadius: 4, + borderBottomLeftRadius: 4, + borderTopRightRadius: 4, + borderTopLeftRadius: 4, + }, + body: { + paddingLeft: 6, + paddingRight: 6, + height: '50%', + width: '100%', + boxSizing: 'border-box', + }, + marketShareBar: { + height: 14, + width: '100%', + backgroundColor: colors.mediumBlue, + }, + subLabel: { + fontSize: 12, + color: colors.lightGrey, + }, + relayerNameLabel: { + fontSize: 16, + fontWeight: 'bold', + color: colors.black, + }, +}; + +export const RelayerGridTile: React.StatelessComponent<RelayerGridTileProps> = (props: RelayerGridTileProps) => { + return ( + <GridTile style={styles.root}> + <div style={styles.innerDiv}> + <img src={props.relayerInfo.headerUrl} style={styles.header} /> + <div style={styles.body}> + <div className="py1" style={styles.relayerNameLabel}> + {props.relayerInfo.name} + </div> + <div style={styles.marketShareBar} /> + <div className="py1" style={styles.subLabel}> + Market share + </div> + <TopTokens tokens={props.relayerInfo.topTokens} networkId={props.networkId} /> + <div className="py1" style={styles.subLabel}> + Top tokens + </div> + </div> + </div> + </GridTile> + ); +}; diff --git a/packages/website/ts/components/relayer_index/relayer_index.tsx b/packages/website/ts/components/relayer_index/relayer_index.tsx new file mode 100644 index 000000000..db3cf07b5 --- /dev/null +++ b/packages/website/ts/components/relayer_index/relayer_index.tsx @@ -0,0 +1,118 @@ +import { colors, Styles } from '@0xproject/react-shared'; +import { GridList } from 'material-ui/GridList'; +import * as React from 'react'; + +import { RelayerGridTile } from 'ts/components/relayer_index/relayer_grid_tile'; +import { RelayerInfo } from 'ts/types'; + +export interface RelayerIndexProps { + networkId: number; +} + +const styles: Styles = { + root: { + width: '100%', + }, + item: { + backgroundColor: colors.white, + borderBottomRightRadius: 10, + borderBottomLeftRadius: 10, + borderTopRightRadius: 10, + borderTopLeftRadius: 10, + boxShadow: `0px 4px 6px ${colors.walletBoxShadow}`, + overflow: 'hidden', + padding: 4, + }, +}; + +// TODO: replace fake data with real, remote data +const topTokens = [ + { + address: '0x1dad4783cf3fe3085c1426157ab175a6119a04ba', + decimals: 18, + iconUrl: '/images/token_icons/makerdao.png', + isRegistered: true, + isTracked: true, + name: 'Maker DAO', + symbol: 'MKR', + }, + { + address: '0x323b5d4c32345ced77393b3530b1eed0f346429d', + decimals: 18, + iconUrl: '/images/token_icons/melon.png', + isRegistered: true, + isTracked: true, + name: 'Melon Token', + symbol: 'MLN', + }, + { + address: '0xb18845c260f680d5b9d84649638813e342e4f8c9', + decimals: 18, + iconUrl: '/images/token_icons/augur.png', + isRegistered: true, + isTracked: true, + name: 'Augur Reputation Token', + symbol: 'REP', + }, +]; + +const relayerInfos: RelayerInfo[] = [ + { + id: '1', + headerUrl: '/images/og_image.png', + name: 'Radar Relay', + marketShare: 0.5, + topTokens, + }, + { + id: '2', + headerUrl: '/images/og_image.png', + name: 'Paradex', + marketShare: 0.5, + topTokens, + }, + { + id: '3', + headerUrl: '/images/og_image.png', + name: 'yo', + marketShare: 0.5, + topTokens, + }, + { + id: '4', + headerUrl: '/images/og_image.png', + name: 'test', + marketShare: 0.5, + topTokens, + }, + { + id: '5', + headerUrl: '/images/og_image.png', + name: 'blahg', + marketShare: 0.5, + topTokens, + }, + { + id: '6', + headerUrl: '/images/og_image.png', + name: 'hello', + marketShare: 0.5, + topTokens, + }, +]; + +const CELL_HEIGHT = 260; +const NUMBER_OF_COLUMNS = 4; +const GRID_PADDING = 16; + +export const RelayerIndex: React.StatelessComponent<RelayerIndexProps> = (props: RelayerIndexProps) => { + return ( + <div style={styles.root}> + <GridList cellHeight={CELL_HEIGHT} cols={NUMBER_OF_COLUMNS} padding={GRID_PADDING} style={styles.gridList}> + {relayerInfos.map((relayerInfo: RelayerInfo) => ( + <RelayerGridTile key={relayerInfo.id} relayerInfo={relayerInfo} networkId={props.networkId} /> + ))} + </GridList> + </div> + ); +}; diff --git a/packages/website/ts/components/relayer_index/relayer_top_tokens.tsx b/packages/website/ts/components/relayer_index/relayer_top_tokens.tsx new file mode 100644 index 000000000..233590b78 --- /dev/null +++ b/packages/website/ts/components/relayer_index/relayer_top_tokens.tsx @@ -0,0 +1,46 @@ +import { colors, EtherscanLinkSuffixes, Styles, utils as sharedUtils } from '@0xproject/react-shared'; +import * as _ from 'lodash'; +import * as React from 'react'; + +import { TokenIcon } from 'ts/components/ui/token_icon'; +import { Token } from 'ts/types'; + +export interface TopTokensProps { + tokens: Token[]; + networkId: number; +} + +const styles: Styles = { + tokenLabel: { + textDecoration: 'none', + color: colors.mediumBlue, + }, + followingTokenLabel: { + paddingLeft: 16, + }, +}; + +export const TopTokens: React.StatelessComponent<TopTokensProps> = (props: TopTokensProps) => { + return ( + <div className="flex"> + {_.map(props.tokens, (token: Token, index: number) => { + const firstItemStyle = { ...styles.tokenLabel, ...styles.followingTokenLabel }; + const style = index !== 0 ? firstItemStyle : styles.tokenLabel; + return ( + <a + key={token.address} + href={tokenLinkFromToken(token, props.networkId)} + target="_blank" + style={style} + > + {token.symbol} + </a> + ); + })} + </div> + ); +}; + +function tokenLinkFromToken(token: Token, networkId: number) { + return sharedUtils.getEtherScanLinkIfExists(token.address, networkId, EtherscanLinkSuffixes.Address); +} diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx index 39c95d31c..d3dc8e3dd 100644 --- a/packages/website/ts/components/wallet/wallet.tsx +++ b/packages/website/ts/components/wallet/wallet.tsx @@ -71,7 +71,7 @@ interface AccessoryItemConfig { } const styles: Styles = { - wallet: { + root: { width: 346, backgroundColor: colors.white, borderBottomRightRadius: 10, @@ -121,6 +121,10 @@ const ZRX_TOKEN_SYMBOL = 'ZRX'; const ETHER_SYMBOL = 'ETH'; const ICON_DIMENSION = 24; const TOKEN_AMOUNT_DISPLAY_PRECISION = 3; +const HEADER_ITEM_KEY = 'HEADER'; +const FOOTER_ITEM_KEY = 'FOOTER'; +const DISCONNECTED_ITEM_KEY = 'DISCONNECTED'; +const ETHER_ITEM_KEY = 'ETHER'; export class Wallet extends React.Component<WalletProps, WalletState> { private _isUnmounted: boolean; @@ -174,7 +178,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> { } public render() { const isReadyToRender = this.props.blockchainIsLoaded && this.props.blockchainErr === BlockchainErrs.NoError; - return <div style={styles.wallet}>{isReadyToRender && this._renderRows()}</div>; + return <div style={styles.root}>{isReadyToRender && this._renderRows()}</div>; } private _renderRows() { const isAddressAvailable = !_.isEmpty(this.props.userAddress); @@ -196,6 +200,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> { const primaryText = 'wallet'; return ( <ListItem + key={HEADER_ITEM_KEY} primaryText={primaryText.toUpperCase()} leftIcon={<ActionAccountBalanceWallet color={colors.mediumBlue} />} style={styles.paddedItem} @@ -206,6 +211,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> { private _renderDisconnectedRows() { return ( <WalletDisconnectedItem + key={DISCONNECTED_ITEM_KEY} providerType={this.props.providerType} injectedProviderName={this.props.injectedProviderName} onToggleLedgerDialog={this.props.onToggleLedgerDialog} @@ -217,6 +223,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> { const primaryText = utils.getAddressBeginAndEnd(userAddress); return ( <ListItem + key={HEADER_ITEM_KEY} primaryText={primaryText} leftIcon={<Identicon address={userAddress} diameter={ICON_DIMENSION} />} style={{ ...styles.paddedItem, ...styles.borderedItem }} @@ -226,7 +233,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> { } private _renderFooterRows() { const primaryText = '+ other tokens'; - return <ListItem primaryText={primaryText} innerDivStyle={styles.footerItemInnerDiv} />; + return <ListItem key={FOOTER_ITEM_KEY} primaryText={primaryText} innerDivStyle={styles.footerItemInnerDiv} />; } private _renderEthRows() { const primaryText = this._renderAmount( @@ -245,7 +252,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> { : { ...styles.tokenItem, ...styles.borderedItem, ...styles.paddedItem }; const etherToken = this._getEthToken(); return ( - <div> + <div key={ETHER_ITEM_KEY}> <ListItem primaryText={primaryText} leftIcon={<img style={{ width: ICON_DIMENSION, height: ICON_DIMENSION }} src={ETHER_ICON_PATH} />} @@ -304,7 +311,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> { : { ...styles.tokenItem, ...styles.borderedItem, ...styles.paddedItem }; const etherToken = this._getEthToken(); return ( - <div> + <div key={token.address}> <ListItem primaryText={amount} leftIcon={this._renderTokenIcon(token, tokenLink)} diff --git a/packages/website/ts/components/wallet/wrap_ether_item.tsx b/packages/website/ts/components/wallet/wrap_ether_item.tsx index 3a876721a..a38163770 100644 --- a/packages/website/ts/components/wallet/wrap_ether_item.tsx +++ b/packages/website/ts/components/wallet/wrap_ether_item.tsx @@ -111,7 +111,7 @@ export class WrapEtherItem extends React.Component<WrapEtherItemProps, WrapEther disableTouchRipple={true} style={walletItemStyles.focusedItem} innerDivStyle={styles.innerDiv} - leftIcon={this.state.isEthConversionHappening && this._renderIsEthConversionHappeningSpinner()} + leftIcon={this._renderIsEthConversionHappeningSpinner()} rightAvatar={this._renderWrapEtherConfirmationButton()} /> ); @@ -123,11 +123,11 @@ export class WrapEtherItem extends React.Component<WrapEtherItemProps, WrapEther }); } private _renderIsEthConversionHappeningSpinner() { - return ( + return this.state.isEthConversionHappening ? ( <div className="pl1" style={{ paddingTop: 10 }}> <i className="zmdi zmdi-spinner zmdi-hc-spin" /> </div> - ); + ) : null; } private _renderWrapEtherConfirmationButton() { const isWrappingEth = this.props.direction === Side.Deposit; diff --git a/packages/website/ts/containers/web3_wrapper_documentation.ts b/packages/website/ts/containers/web3_wrapper_documentation.ts index 289006f10..b0adad536 100644 --- a/packages/website/ts/containers/web3_wrapper_documentation.ts +++ b/packages/website/ts/containers/web3_wrapper_documentation.ts @@ -39,7 +39,7 @@ const docsInfoConfig: DocsInfoConfig = { [docSections.installation]: InstallationMarkdown, }, sectionNameToModulePath: { - [docSections.web3Wrapper]: ['"web3-wrapper/src/index"'], + [docSections.web3Wrapper]: ['"web3-wrapper/src/web3_wrapper"'], [docSections.types]: ['"types/src/index"'], }, menuSubsectionToVersionWhenIntroduced: {}, diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index 901483327..dba59f704 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -490,4 +490,12 @@ export interface TokenState { allowance: BigNumber; isLoaded: boolean; } + +export interface RelayerInfo { + headerUrl: string; + id: string; + name: string; + marketShare: number; + topTokens: Token[]; +} // tslint:disable:max-file-line-count |