merge development
This commit is contained in:
@@ -31,7 +31,7 @@ jobs:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: cd packages/website && yarn build
|
||||
- run: cd packages/website && yarn build:prod
|
||||
test-contracts-ganache:
|
||||
docker:
|
||||
- image: circleci/node:9
|
||||
@@ -162,6 +162,9 @@ jobs:
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: circleci/python
|
||||
- image: 0xorg/ganache-cli
|
||||
command: |
|
||||
ganache-cli --gasLimit 10000000 --noVMErrorsOnRPCResponse --db /snapshot --noVMErrorsOnRPCResponse -p 8545 --networkId 50 -m "concert load couple harbor equip island argue ramp clarify fence smart topic"
|
||||
steps:
|
||||
- checkout
|
||||
- run: sudo chown -R circleci:circleci /usr/local/bin
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -99,6 +99,7 @@ packages/*/scripts/
|
||||
.mypy_cache
|
||||
.tox
|
||||
python-packages/*/build
|
||||
python-packages/*/dist
|
||||
__pycache__
|
||||
python-packages/*/src/*.egg-info
|
||||
python-packages/*/.coverage
|
||||
|
||||
@@ -4,6 +4,7 @@ lib
|
||||
/packages/contracts/generated-artifacts
|
||||
/packages/abi-gen-wrappers/src/generated-wrappers
|
||||
/packages/contract-artifacts/artifacts
|
||||
/python-packages/order_utils/src/zero_ex/contract_artifacts/artifacts
|
||||
/packages/json-schemas/schemas
|
||||
/packages/metacoin/src/contract_wrappers
|
||||
/packages/metacoin/artifacts
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
},
|
||||
{
|
||||
"path": "packages/instant/public/main.bundle.js",
|
||||
"maxSize": "500kB"
|
||||
"maxSize": "1000kB"
|
||||
}
|
||||
],
|
||||
"ci": {
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "2.0.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1541740904
|
||||
},
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"changes": [
|
||||
|
||||
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v2.0.1 - _November 9, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.0.0 - _October 18, 2018_
|
||||
|
||||
* Add support for `eth_signTypedData`. (#1102)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "0x.js",
|
||||
"version": "2.0.0",
|
||||
"version": "2.0.1",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -18,7 +18,7 @@
|
||||
"build": "yarn build:all",
|
||||
"build:ci": "yarn build:commonjs",
|
||||
"build:all": "run-p build:umd:prod build:commonjs",
|
||||
"lint": "tslint --project . --exclude **/src/generated_contract_wrappers/**/*",
|
||||
"lint": "tslint --format stylish --project .",
|
||||
"test:circleci": "run-s test:coverage",
|
||||
"rebuild_and_test": "run-s build test",
|
||||
"test:coverage": "nyc npm run test --all && yarn coverage:report:lcov",
|
||||
@@ -42,12 +42,12 @@
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^1.0.14",
|
||||
"@0x/abi-gen-wrappers": "^1.0.1",
|
||||
"@0x/contract-addresses": "^1.0.1",
|
||||
"@0x/dev-utils": "^1.0.13",
|
||||
"@0x/migrations": "^2.0.0",
|
||||
"@0x/tslint-config": "^1.0.9",
|
||||
"@0x/abi-gen": "^1.0.15",
|
||||
"@0x/abi-gen-wrappers": "^1.0.2",
|
||||
"@0x/contract-addresses": "^1.1.0",
|
||||
"@0x/dev-utils": "^1.0.14",
|
||||
"@0x/migrations": "^2.0.1",
|
||||
"@0x/tslint-config": "^1.0.10",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^2.2.42",
|
||||
"@types/node": "*",
|
||||
@@ -73,18 +73,18 @@
|
||||
"webpack": "^4.20.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/assert": "^1.0.14",
|
||||
"@0x/base-contract": "^3.0.2",
|
||||
"@0x/contract-wrappers": "^3.0.0",
|
||||
"@0x/order-utils": "^2.0.0",
|
||||
"@0x/order-watcher": "^2.2.0",
|
||||
"@0x/subproviders": "^2.1.0",
|
||||
"@0x/types": "^1.2.0",
|
||||
"@0x/typescript-typings": "^3.0.3",
|
||||
"@0x/utils": "^2.0.3",
|
||||
"@0x/web3-wrapper": "^3.1.0",
|
||||
"@0x/assert": "^1.0.15",
|
||||
"@0x/base-contract": "^3.0.3",
|
||||
"@0x/contract-wrappers": "^3.0.1",
|
||||
"@0x/order-utils": "^2.0.1",
|
||||
"@0x/order-watcher": "^2.2.1",
|
||||
"@0x/subproviders": "^2.1.1",
|
||||
"@0x/types": "^1.2.1",
|
||||
"@0x/typescript-typings": "^3.0.4",
|
||||
"@0x/utils": "^2.0.4",
|
||||
"@0x/web3-wrapper": "^3.1.1",
|
||||
"@types/web3-provider-engine": "^14.0.0",
|
||||
"ethereum-types": "^1.1.1",
|
||||
"ethereum-types": "^1.1.2",
|
||||
"ethers": "~4.0.4",
|
||||
"lodash": "^4.17.5",
|
||||
"web3-provider-engine": "14.0.6"
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "1.0.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1541740904
|
||||
},
|
||||
{
|
||||
"timestamp": 1539871071,
|
||||
"version": "1.0.1",
|
||||
|
||||
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.0.2 - _November 9, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.1 - _October 18, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/abi-gen-wrappers",
|
||||
"version": "1.0.1",
|
||||
"version": "1.0.2",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -12,7 +12,7 @@
|
||||
"scripts": {
|
||||
"build": "yarn pre_build && tsc -b",
|
||||
"build:ci": "yarn build",
|
||||
"lint": "tslint --project .",
|
||||
"lint": "tslint --format stylish --project .",
|
||||
"pre_build": "yarn generate_contract_wrappers",
|
||||
"clean": "shx rm -rf lib wrappers",
|
||||
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output src/generated-wrappers --backend ethers"
|
||||
@@ -30,17 +30,17 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/packages/abi-gen-wrappers/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^1.0.14",
|
||||
"@0x/tslint-config": "^1.0.9",
|
||||
"@0x/utils": "^2.0.3",
|
||||
"@0x/web3-wrapper": "^3.1.0",
|
||||
"ethereum-types": "^1.1.1",
|
||||
"@0x/abi-gen": "^1.0.15",
|
||||
"@0x/tslint-config": "^1.0.10",
|
||||
"@0x/utils": "^2.0.4",
|
||||
"@0x/web3-wrapper": "^3.1.1",
|
||||
"ethereum-types": "^1.1.2",
|
||||
"ethers": "~4.0.4",
|
||||
"lodash": "^4.17.5",
|
||||
"shx": "^0.2.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^3.0.2"
|
||||
"@0x/base-contract": "^3.0.3"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "1.0.15",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1541740904
|
||||
},
|
||||
{
|
||||
"timestamp": 1539871071,
|
||||
"version": "1.0.14",
|
||||
|
||||
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.0.15 - _November 9, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.14 - _October 18, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/abi-gen",
|
||||
"version": "1.0.14",
|
||||
"version": "1.0.15",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -8,7 +8,7 @@
|
||||
"main": "lib/src/index.js",
|
||||
"types": "lib/src/index.d.ts",
|
||||
"scripts": {
|
||||
"lint": "tslint --project .",
|
||||
"lint": "tslint --format stylish --project .",
|
||||
"clean": "shx rm -rf lib",
|
||||
"build": "tsc -b",
|
||||
"build:ci": "yarn build",
|
||||
@@ -31,10 +31,10 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/packages/abi-gen/README.md",
|
||||
"dependencies": {
|
||||
"@0x/typescript-typings": "^3.0.3",
|
||||
"@0x/utils": "^2.0.3",
|
||||
"@0x/typescript-typings": "^3.0.4",
|
||||
"@0x/utils": "^2.0.4",
|
||||
"chalk": "^2.3.0",
|
||||
"ethereum-types": "^1.1.1",
|
||||
"ethereum-types": "^1.1.2",
|
||||
"glob": "^7.1.2",
|
||||
"handlebars": "^4.0.11",
|
||||
"lodash": "^4.17.5",
|
||||
@@ -45,7 +45,7 @@
|
||||
"yargs": "^10.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@0x/tslint-config": "^1.0.9",
|
||||
"@0x/tslint-config": "^1.0.10",
|
||||
"@types/glob": "5.0.35",
|
||||
"@types/handlebars": "^4.0.36",
|
||||
"@types/mkdirp": "^0.5.1",
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "1.0.15",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1541740904
|
||||
},
|
||||
{
|
||||
"timestamp": 1539871071,
|
||||
"version": "1.0.14",
|
||||
|
||||
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.0.15 - _November 9, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.14 - _October 18, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/assert",
|
||||
"version": "1.0.14",
|
||||
"version": "1.0.15",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -11,7 +11,7 @@
|
||||
"build": "tsc -b",
|
||||
"build:ci": "yarn build",
|
||||
"clean": "shx rm -rf lib test_temp",
|
||||
"lint": "tslint --project .",
|
||||
"lint": "tslint --format stylish --project .",
|
||||
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js --exit",
|
||||
"test": "yarn run_mocha",
|
||||
"rebuild_and_test": "run-s clean build test",
|
||||
@@ -29,7 +29,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/packages/assert/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/tslint-config": "^1.0.9",
|
||||
"@0x/tslint-config": "^1.0.10",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^2.2.42",
|
||||
"@types/valid-url": "^1.0.2",
|
||||
@@ -44,9 +44,9 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/json-schemas": "^2.0.0",
|
||||
"@0x/typescript-typings": "^3.0.3",
|
||||
"@0x/utils": "^2.0.3",
|
||||
"@0x/json-schemas": "^2.0.1",
|
||||
"@0x/typescript-typings": "^3.0.4",
|
||||
"@0x/utils": "^2.0.4",
|
||||
"lodash": "^4.17.5",
|
||||
"valid-url": "^1.0.9"
|
||||
},
|
||||
|
||||
@@ -1,4 +1,37 @@
|
||||
[
|
||||
{
|
||||
"version": "2.2.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "`getAssetBuyerForProvidedOrders` factory function now takes 3 args instead of 4",
|
||||
"pr": 1187
|
||||
},
|
||||
{
|
||||
"note":
|
||||
"the `OrderProvider` now requires a new method `getAvailableMakerAssetDatasAsync` and the `StandardRelayerAPIOrderProvider` requires the network id at init.",
|
||||
"pr": 1203
|
||||
},
|
||||
{
|
||||
"note": "No longer require that provided orders all have the same maker and taker asset data",
|
||||
"pr": 1197
|
||||
},
|
||||
{
|
||||
"note":
|
||||
"Fix bug where `BuyQuoteInfo` objects could return `totalEthAmount` and `feeEthAmount` that were not whole numbers",
|
||||
"pr": 1207
|
||||
},
|
||||
{
|
||||
"note":
|
||||
"Fix bug where default values for `AssetBuyer` public facing methods could get overriden by `undefined` values",
|
||||
"pr": 1207
|
||||
},
|
||||
{
|
||||
"note": "Lower default expiry buffer from 5 minutes to 2 minutes",
|
||||
"pr": 1217
|
||||
}
|
||||
],
|
||||
"timestamp": 1541740904
|
||||
},
|
||||
{
|
||||
"version": "2.1.0",
|
||||
"changes": [
|
||||
|
||||
@@ -5,6 +5,15 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v2.2.0 - _November 9, 2018_
|
||||
|
||||
* `getAssetBuyerForProvidedOrders` factory function now takes 3 args instead of 4 (#1187)
|
||||
* the `OrderProvider` now requires a new method `getAvailableMakerAssetDatasAsync` and the `StandardRelayerAPIOrderProvider` requires the network id at init. (#1203)
|
||||
* No longer require that provided orders all have the same maker and taker asset data (#1197)
|
||||
* Fix bug where `BuyQuoteInfo` objects could return `totalEthAmount` and `feeEthAmount` that were not whole numbers (#1207)
|
||||
* Fix bug where default values for `AssetBuyer` public facing methods could get overriden by `undefined` values (#1207)
|
||||
* Lower default expiry buffer from 5 minutes to 2 minutes (#1217)
|
||||
|
||||
## v2.1.0 - _October 18, 2018_
|
||||
|
||||
* Add `gasLimit` and `gasPrice` as optional properties on `BuyQuoteExecutionOpts`
|
||||
@@ -13,6 +22,7 @@ CHANGELOG
|
||||
* Add `gasLimit` and `gasPrice` as optional properties on `BuyQuoteExecutionOpts` (#1116)
|
||||
* Add `docs:json` command to package.json (#1139)
|
||||
* Add missing types to public interface (#1139)
|
||||
* Throw `SignatureRequestDenied` and `TransactionValueTooLow` errors when executing buy (#1147)
|
||||
|
||||
## v2.0.0 - _October 4, 2018_
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/asset-buyer",
|
||||
"version": "2.1.0",
|
||||
"version": "2.2.0",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -10,7 +10,7 @@
|
||||
"scripts": {
|
||||
"build": "yarn tsc -b",
|
||||
"build:ci": "yarn build",
|
||||
"lint": "tslint --project .",
|
||||
"lint": "tslint --format stylish --project .",
|
||||
"test": "yarn run_mocha",
|
||||
"rebuild_and_test": "run-s clean build test",
|
||||
"test:coverage": "nyc npm run test --all && yarn coverage:report:lcov",
|
||||
@@ -36,21 +36,21 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/packages/asset-buyer/README.md",
|
||||
"dependencies": {
|
||||
"@0x/assert": "^1.0.14",
|
||||
"@0x/connect": "^3.0.2",
|
||||
"@0x/contract-wrappers": "^3.0.0",
|
||||
"@0x/json-schemas": "^2.0.0",
|
||||
"@0x/order-utils": "^2.0.0",
|
||||
"@0x/subproviders": "^2.1.0",
|
||||
"@0x/types": "^1.2.0",
|
||||
"@0x/typescript-typings": "^3.0.3",
|
||||
"@0x/utils": "^2.0.3",
|
||||
"@0x/web3-wrapper": "^3.1.0",
|
||||
"ethereum-types": "^1.1.1",
|
||||
"@0x/assert": "^1.0.15",
|
||||
"@0x/connect": "^3.0.3",
|
||||
"@0x/contract-wrappers": "^3.0.1",
|
||||
"@0x/json-schemas": "^2.0.1",
|
||||
"@0x/order-utils": "^2.0.1",
|
||||
"@0x/subproviders": "^2.1.1",
|
||||
"@0x/types": "^1.2.1",
|
||||
"@0x/typescript-typings": "^3.0.4",
|
||||
"@0x/utils": "^2.0.4",
|
||||
"@0x/web3-wrapper": "^3.1.1",
|
||||
"ethereum-types": "^1.1.2",
|
||||
"lodash": "^4.17.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@0x/tslint-config": "^1.0.9",
|
||||
"@0x/tslint-config": "^1.0.10",
|
||||
"@types/lodash": "^4.14.116",
|
||||
"@types/mocha": "^2.2.42",
|
||||
"@types/node": "*",
|
||||
|
||||
@@ -52,16 +52,12 @@ export class AssetBuyer {
|
||||
public static getAssetBuyerForProvidedOrders(
|
||||
provider: Provider,
|
||||
orders: SignedOrder[],
|
||||
feeOrders: SignedOrder[] = [],
|
||||
options: Partial<AssetBuyerOpts> = {},
|
||||
): AssetBuyer {
|
||||
assert.isWeb3Provider('provider', provider);
|
||||
assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema);
|
||||
assert.doesConformToSchema('feeOrders', feeOrders, schemas.signedOrdersSchema);
|
||||
assert.areValidProvidedOrders('orders', orders);
|
||||
assert.areValidProvidedOrders('feeOrders', feeOrders);
|
||||
assert.assert(orders.length !== 0, `Expected orders to contain at least one order`);
|
||||
const orderProvider = new BasicOrderProvider(_.concat(orders, feeOrders));
|
||||
const orderProvider = new BasicOrderProvider(orders);
|
||||
const assetBuyer = new AssetBuyer(provider, orderProvider, options);
|
||||
return assetBuyer;
|
||||
}
|
||||
@@ -80,7 +76,8 @@ export class AssetBuyer {
|
||||
): AssetBuyer {
|
||||
assert.isWeb3Provider('provider', provider);
|
||||
assert.isWebUri('sraApiUrl', sraApiUrl);
|
||||
const orderProvider = new StandardRelayerAPIOrderProvider(sraApiUrl);
|
||||
const networkId = options.networkId || constants.DEFAULT_ASSET_BUYER_OPTS.networkId;
|
||||
const orderProvider = new StandardRelayerAPIOrderProvider(sraApiUrl, networkId);
|
||||
const assetBuyer = new AssetBuyer(provider, orderProvider, options);
|
||||
return assetBuyer;
|
||||
}
|
||||
@@ -93,10 +90,11 @@ export class AssetBuyer {
|
||||
* @return An instance of AssetBuyer
|
||||
*/
|
||||
constructor(provider: Provider, orderProvider: OrderProvider, options: Partial<AssetBuyerOpts> = {}) {
|
||||
const { networkId, orderRefreshIntervalMs, expiryBufferSeconds } = {
|
||||
...constants.DEFAULT_ASSET_BUYER_OPTS,
|
||||
...options,
|
||||
};
|
||||
const { networkId, orderRefreshIntervalMs, expiryBufferSeconds } = _.merge(
|
||||
{},
|
||||
constants.DEFAULT_ASSET_BUYER_OPTS,
|
||||
options,
|
||||
);
|
||||
assert.isWeb3Provider('provider', provider);
|
||||
assert.isValidOrderProvider('orderProvider', orderProvider);
|
||||
assert.isNumber('networkId', networkId);
|
||||
@@ -125,10 +123,11 @@ export class AssetBuyer {
|
||||
assetBuyAmount: BigNumber,
|
||||
options: Partial<BuyQuoteRequestOpts> = {},
|
||||
): Promise<BuyQuote> {
|
||||
const { feePercentage, shouldForceOrderRefresh, slippagePercentage } = {
|
||||
...constants.DEFAULT_BUY_QUOTE_REQUEST_OPTS,
|
||||
...options,
|
||||
};
|
||||
const { feePercentage, shouldForceOrderRefresh, slippagePercentage } = _.merge(
|
||||
{},
|
||||
constants.DEFAULT_BUY_QUOTE_REQUEST_OPTS,
|
||||
options,
|
||||
);
|
||||
assert.isString('assetData', assetData);
|
||||
assert.isBigNumber('assetBuyAmount', assetBuyAmount);
|
||||
assert.isValidPercentage('feePercentage', feePercentage);
|
||||
@@ -189,10 +188,11 @@ export class AssetBuyer {
|
||||
buyQuote: BuyQuote,
|
||||
options: Partial<BuyQuoteExecutionOpts> = {},
|
||||
): Promise<string> {
|
||||
const { ethAmount, takerAddress, feeRecipient, gasLimit, gasPrice } = {
|
||||
...constants.DEFAULT_BUY_QUOTE_EXECUTION_OPTS,
|
||||
...options,
|
||||
};
|
||||
const { ethAmount, takerAddress, feeRecipient, gasLimit, gasPrice } = _.merge(
|
||||
{},
|
||||
constants.DEFAULT_BUY_QUOTE_EXECUTION_OPTS,
|
||||
options,
|
||||
);
|
||||
assert.isValidBuyQuote('buyQuote', buyQuote);
|
||||
if (!_.isUndefined(ethAmount)) {
|
||||
assert.isBigNumber('ethAmount', ethAmount);
|
||||
@@ -201,6 +201,12 @@ export class AssetBuyer {
|
||||
assert.isETHAddressHex('takerAddress', takerAddress);
|
||||
}
|
||||
assert.isETHAddressHex('feeRecipient', feeRecipient);
|
||||
if (!_.isUndefined(gasLimit)) {
|
||||
assert.isNumber('gasLimit', gasLimit);
|
||||
}
|
||||
if (!_.isUndefined(gasPrice)) {
|
||||
assert.isBigNumber('gasPrice', gasPrice);
|
||||
}
|
||||
const { orders, feeOrders, feePercentage, assetBuyAmount, worstCaseQuoteInfo } = buyQuote;
|
||||
// if no takerAddress is provided, try to get one from the provider
|
||||
let finalTakerAddress;
|
||||
@@ -243,6 +249,15 @@ export class AssetBuyer {
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get the asset data of all assets that are purchaseable with ether token (wETH) in the order provider passed in at init.
|
||||
*
|
||||
* @return An array of asset data strings that can be purchased using wETH.
|
||||
*/
|
||||
public async getAvailableAssetDatasAsync(): Promise<string[]> {
|
||||
const etherTokenAssetData = this._getEtherTokenAssetDataOrThrow();
|
||||
return this.orderProvider.getAvailableMakerAssetDatasAsync(etherTokenAssetData);
|
||||
}
|
||||
/**
|
||||
* Grab orders from the map, if there is a miss or it is time to refresh, fetch and process the orders
|
||||
*/
|
||||
|
||||
@@ -9,7 +9,7 @@ const MAINNET_NETWORK_ID = 1;
|
||||
const DEFAULT_ASSET_BUYER_OPTS: AssetBuyerOpts = {
|
||||
networkId: MAINNET_NETWORK_ID,
|
||||
orderRefreshIntervalMs: 10000, // 10 seconds
|
||||
expiryBufferSeconds: 300, // 5 minutes
|
||||
expiryBufferSeconds: 120, // 2 minutes
|
||||
};
|
||||
|
||||
const DEFAULT_BUY_QUOTE_REQUEST_OPTS: BuyQuoteRequestOpts = {
|
||||
@@ -36,6 +36,5 @@ export const constants = {
|
||||
DEFAULT_ASSET_BUYER_OPTS,
|
||||
DEFAULT_BUY_QUOTE_EXECUTION_OPTS,
|
||||
DEFAULT_BUY_QUOTE_REQUEST_OPTS,
|
||||
MAX_PER_PAGE: 10000,
|
||||
EMPTY_ORDERS_AND_FILLABLE_AMOUNTS,
|
||||
};
|
||||
|
||||
@@ -29,4 +29,13 @@ export class BasicOrderProvider implements OrderProvider {
|
||||
});
|
||||
return { orders };
|
||||
}
|
||||
/**
|
||||
* Given a taker asset data string, return all availabled paired maker asset data strings.
|
||||
* @param takerAssetData A string representing the taker asset data.
|
||||
* @return An array of asset data strings that can be purchased using takerAssetData.
|
||||
*/
|
||||
public async getAvailableMakerAssetDatasAsync(takerAssetData: string): Promise<string[]> {
|
||||
const ordersWithTakerAssetData = _.filter(this.orders, { takerAssetData });
|
||||
return _.map(ordersWithTakerAssetData, order => order.makerAssetData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { HttpClient } from '@0x/connect';
|
||||
import { APIOrder, OrderbookResponse } from '@0x/types';
|
||||
import { APIOrder, AssetPairsResponse, OrderbookResponse } from '@0x/types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import {
|
||||
@@ -14,6 +14,7 @@ import { orderUtils } from '../utils/order_utils';
|
||||
|
||||
export class StandardRelayerAPIOrderProvider implements OrderProvider {
|
||||
public readonly apiUrl: string;
|
||||
public readonly networkId: number;
|
||||
private readonly _sraClient: HttpClient;
|
||||
/**
|
||||
* Given an array of APIOrder objects from a standard relayer api, return an array
|
||||
@@ -44,12 +45,15 @@ export class StandardRelayerAPIOrderProvider implements OrderProvider {
|
||||
}
|
||||
/**
|
||||
* Instantiates a new StandardRelayerAPIOrderProvider instance
|
||||
* @param apiUrl The standard relayer API base HTTP url you would like to source orders from.
|
||||
* @param apiUrl The standard relayer API base HTTP url you would like to source orders from.
|
||||
* @param networkId The ethereum network id.
|
||||
* @return An instance of StandardRelayerAPIOrderProvider
|
||||
*/
|
||||
constructor(apiUrl: string) {
|
||||
constructor(apiUrl: string, networkId: number) {
|
||||
assert.isWebUri('apiUrl', apiUrl);
|
||||
assert.isNumber('networkId', networkId);
|
||||
this.apiUrl = apiUrl;
|
||||
this.networkId = networkId;
|
||||
this._sraClient = new HttpClient(apiUrl);
|
||||
}
|
||||
/**
|
||||
@@ -59,9 +63,9 @@ export class StandardRelayerAPIOrderProvider implements OrderProvider {
|
||||
*/
|
||||
public async getOrdersAsync(orderProviderRequest: OrderProviderRequest): Promise<OrderProviderResponse> {
|
||||
assert.isValidOrderProviderRequest('orderProviderRequest', orderProviderRequest);
|
||||
const { makerAssetData, takerAssetData, networkId } = orderProviderRequest;
|
||||
const { makerAssetData, takerAssetData } = orderProviderRequest;
|
||||
const orderbookRequest = { baseAssetData: makerAssetData, quoteAssetData: takerAssetData };
|
||||
const requestOpts = { networkId };
|
||||
const requestOpts = { networkId: this.networkId };
|
||||
let orderbook: OrderbookResponse;
|
||||
try {
|
||||
orderbook = await this._sraClient.getOrderbookAsync(orderbookRequest, requestOpts);
|
||||
@@ -76,4 +80,26 @@ export class StandardRelayerAPIOrderProvider implements OrderProvider {
|
||||
orders,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Given a taker asset data string, return all availabled paired maker asset data strings.
|
||||
* @param takerAssetData A string representing the taker asset data.
|
||||
* @return An array of asset data strings that can be purchased using takerAssetData.
|
||||
*/
|
||||
public async getAvailableMakerAssetDatasAsync(takerAssetData: string): Promise<string[]> {
|
||||
// Return a maximum of 1000 asset datas
|
||||
const maxPerPage = 1000;
|
||||
const requestOpts = { networkId: this.networkId, perPage: maxPerPage };
|
||||
const assetPairsRequest = { assetDataA: takerAssetData };
|
||||
const fullRequest = {
|
||||
...requestOpts,
|
||||
...assetPairsRequest,
|
||||
};
|
||||
let response: AssetPairsResponse;
|
||||
try {
|
||||
response = await this._sraClient.getAssetPairsAsync(fullRequest);
|
||||
} catch (err) {
|
||||
throw new Error(AssetBuyerError.StandardRelayerApiError);
|
||||
}
|
||||
return _.map(response.records, item => item.assetDataB.assetData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import { BigNumber } from '@0x/utils';
|
||||
export interface OrderProviderRequest {
|
||||
makerAssetData: string;
|
||||
takerAssetData: string;
|
||||
networkId: number;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -27,10 +26,12 @@ export interface SignedOrderWithRemainingFillableMakerAssetAmount extends Signed
|
||||
remainingFillableMakerAssetAmount?: BigNumber;
|
||||
}
|
||||
/**
|
||||
* Given an OrderProviderRequest, get an OrderProviderResponse.
|
||||
* gerOrdersAsync: Given an OrderProviderRequest, get an OrderProviderResponse.
|
||||
* getAvailableMakerAssetDatasAsync: Given a taker asset data string, return all availabled paired maker asset data strings.
|
||||
*/
|
||||
export interface OrderProvider {
|
||||
getOrdersAsync: (orderProviderRequest: OrderProviderRequest) => Promise<OrderProviderResponse>;
|
||||
getAvailableMakerAssetDatasAsync: (takerAssetData: string) => Promise<string[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { assert as sharedAssert } from '@0x/assert';
|
||||
import { schemas } from '@0x/json-schemas';
|
||||
import { SignedOrder } from '@0x/types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { BuyQuote, BuyQuoteInfo, OrderProvider, OrderProviderRequest } from '../types';
|
||||
@@ -29,22 +28,6 @@ export const assert = {
|
||||
isValidOrderProviderRequest(variableName: string, orderFetcherRequest: OrderProviderRequest): void {
|
||||
sharedAssert.isHexString(`${variableName}.makerAssetData`, orderFetcherRequest.makerAssetData);
|
||||
sharedAssert.isHexString(`${variableName}.takerAssetData`, orderFetcherRequest.takerAssetData);
|
||||
sharedAssert.isNumber(`${variableName}.networkId`, orderFetcherRequest.networkId);
|
||||
},
|
||||
areValidProvidedOrders(variableName: string, orders: SignedOrder[]): void {
|
||||
if (orders.length === 0) {
|
||||
return;
|
||||
}
|
||||
const makerAssetData = orders[0].makerAssetData;
|
||||
const takerAssetData = orders[0].takerAssetData;
|
||||
const filteredOrders = _.filter(
|
||||
orders,
|
||||
order => order.makerAssetData === makerAssetData && order.takerAssetData === takerAssetData,
|
||||
);
|
||||
sharedAssert.assert(
|
||||
orders.length === filteredOrders.length,
|
||||
`Expected all orders in ${variableName} to have the same makerAssetData and takerAssetData.`,
|
||||
);
|
||||
},
|
||||
isValidPercentage(variableName: string, percentage: number): void {
|
||||
assert.isNumber(variableName, percentage);
|
||||
|
||||
@@ -119,7 +119,7 @@ function calculateQuoteInfo(
|
||||
ethAmountToBuyZrx = findEthAmountNeededToBuyZrx(feeOrdersAndFillableAmounts, zrxAmountToBuyAsset);
|
||||
}
|
||||
/// find the eth amount needed to buy the affiliate fee
|
||||
const ethAmountToBuyAffiliateFee = ethAmountToBuyAsset.mul(feePercentage);
|
||||
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
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "3.0.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1541740904
|
||||
},
|
||||
{
|
||||
"timestamp": 1539871071,
|
||||
"version": "3.0.2",
|
||||
|
||||
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v3.0.3 - _November 9, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.0.2 - _October 18, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/base-contract",
|
||||
"version": "3.0.2",
|
||||
"version": "3.0.3",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -17,7 +17,7 @@
|
||||
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js --bail --exit",
|
||||
"test:coverage": "nyc npm run test --all && yarn coverage:report:lcov",
|
||||
"coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info",
|
||||
"lint": "tslint --project . --exclude **/src/contract_wrappers/**/*"
|
||||
"lint": "tslint --format stylish --project ."
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
@@ -29,7 +29,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/packages/base-contract/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/tslint-config": "^1.0.9",
|
||||
"@0x/tslint-config": "^1.0.10",
|
||||
"@types/lodash": "4.14.104",
|
||||
"chai": "^4.0.1",
|
||||
"make-promises-safe": "^1.1.0",
|
||||
@@ -40,10 +40,10 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/typescript-typings": "^3.0.3",
|
||||
"@0x/utils": "^2.0.3",
|
||||
"@0x/web3-wrapper": "^3.1.0",
|
||||
"ethereum-types": "^1.1.1",
|
||||
"@0x/typescript-typings": "^3.0.4",
|
||||
"@0x/utils": "^2.0.4",
|
||||
"@0x/web3-wrapper": "^3.1.1",
|
||||
"ethereum-types": "^1.1.2",
|
||||
"ethers": "~4.0.4",
|
||||
"lodash": "^4.17.5"
|
||||
},
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "3.0.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1541740904
|
||||
},
|
||||
{
|
||||
"timestamp": 1539871071,
|
||||
"version": "3.0.2",
|
||||
|
||||
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v3.0.3 - _November 9, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.0.2 - _October 18, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/connect",
|
||||
"version": "3.0.2",
|
||||
"version": "3.0.3",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -19,7 +19,7 @@
|
||||
"build:ci": "yarn build",
|
||||
"clean": "shx rm -rf lib test_temp generated_docs",
|
||||
"copy_test_fixtures": "copyfiles -u 2 './test/fixtures/**/*.json' ./lib/test/fixtures",
|
||||
"lint": "tslint --project .",
|
||||
"lint": "tslint --format stylish --project .",
|
||||
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js --exit",
|
||||
"test": "run-s copy_test_fixtures run_mocha",
|
||||
"rebuild_and_test": "run-s clean build test",
|
||||
@@ -44,12 +44,12 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/packages/connect/README.md",
|
||||
"dependencies": {
|
||||
"@0x/assert": "^1.0.14",
|
||||
"@0x/json-schemas": "^2.0.0",
|
||||
"@0x/order-utils": "^2.0.0",
|
||||
"@0x/types": "^1.2.0",
|
||||
"@0x/typescript-typings": "^3.0.3",
|
||||
"@0x/utils": "^2.0.3",
|
||||
"@0x/assert": "^1.0.15",
|
||||
"@0x/json-schemas": "^2.0.1",
|
||||
"@0x/order-utils": "^2.0.1",
|
||||
"@0x/types": "^1.2.1",
|
||||
"@0x/typescript-typings": "^3.0.4",
|
||||
"@0x/utils": "^2.0.4",
|
||||
"lodash": "^4.17.5",
|
||||
"query-string": "^5.0.1",
|
||||
"sinon": "^4.0.0",
|
||||
@@ -57,7 +57,7 @@
|
||||
"websocket": "^1.0.25"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@0x/tslint-config": "^1.0.9",
|
||||
"@0x/tslint-config": "^1.0.10",
|
||||
"@types/fetch-mock": "^6.0.3",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^2.2.42",
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
[
|
||||
{
|
||||
"version": "1.1.0",
|
||||
"changes": [
|
||||
{
|
||||
"pr": 1192,
|
||||
"note": "Update Forwarder addresses"
|
||||
}
|
||||
],
|
||||
"timestamp": 1541740904
|
||||
},
|
||||
{
|
||||
"version": "1.0.1",
|
||||
"changes": [
|
||||
|
||||
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.1.0 - _November 9, 2018_
|
||||
|
||||
* Update Forwarder addresses (#1192)
|
||||
|
||||
## v1.0.1 - _October 18, 2018_
|
||||
|
||||
* Initial release (#1105)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contract-addresses",
|
||||
"version": "1.0.1",
|
||||
"version": "1.1.0",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
|
||||
@@ -25,7 +25,7 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = {
|
||||
etherToken: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
|
||||
exchange: '0x4f833a24e1f95d70f028921e27040ca56e09ab0b',
|
||||
assetProxyOwner: '0x17992e4ffb22730138e4b62aaa6367fa9d3699a6',
|
||||
forwarder: '0x7afc2d5107af94c462a194d2c21b5bdd238709d6',
|
||||
forwarder: '0x5468a1dc173652ee28d249c271fa9933144746b1',
|
||||
orderValidator: '0x9463e518dea6810309563c81d5266c1b1d149138',
|
||||
},
|
||||
3: {
|
||||
@@ -35,7 +35,7 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = {
|
||||
etherToken: '0xc778417e063141139fce010982780140aa0cd5ab',
|
||||
exchange: '0x4530c0483a1633c7a1c97d2c53721caff2caaaaf',
|
||||
assetProxyOwner: '0xf5fa5b5fed2727a0e44ac67f6772e97977aa358b',
|
||||
forwarder: '0x3983e204b12b3c02fb0638caf2cd406a62e0ead3',
|
||||
forwarder: '0x2240dab907db71e64d3e0dba4800c83b5c502d4e',
|
||||
orderValidator: '0x90431a90516ab49af23a0530e04e8c7836e7122f',
|
||||
},
|
||||
42: {
|
||||
@@ -45,7 +45,7 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = {
|
||||
etherToken: '0xd0a1e359811322d97991e03f863a0c30c2cf029c',
|
||||
exchange: '0x35dd2932454449b14cee11a94d3674a936d5d7b2',
|
||||
assetProxyOwner: '0x2c824d2882baa668e0d5202b1e7f2922278703f8',
|
||||
forwarder: '0xd85e2fa7e7e252b27b01bf0d65c946959d2f45b8',
|
||||
forwarder: '0x17992e4ffb22730138e4b62aaa6367fa9d3699a6',
|
||||
orderValidator: '0xb389da3d204b412df2f75c6afb3d0a7ce0bc283d',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
[
|
||||
{
|
||||
"version": "1.1.0",
|
||||
"changes": [
|
||||
{
|
||||
"pr": 1192,
|
||||
"note": "Update Forwarder artifact"
|
||||
}
|
||||
],
|
||||
"timestamp": 1541740904
|
||||
},
|
||||
{
|
||||
"version": "1.0.1",
|
||||
"changes": [
|
||||
|
||||
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.1.0 - _November 9, 2018_
|
||||
|
||||
* Update Forwarder artifact (#1192)
|
||||
|
||||
## v1.0.1 - _October 18, 2018_
|
||||
|
||||
* Initial release (#1105)
|
||||
|
||||
887
packages/contract-artifacts/artifacts/Forwarder.json
generated
887
packages/contract-artifacts/artifacts/Forwarder.json
generated
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contract-artifacts",
|
||||
"version": "1.0.1",
|
||||
"version": "1.1.0",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
[
|
||||
{
|
||||
"version": "3.0.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Fix bug in `ForwarderWrapper` where `feeRecipientAddress` was not correctly normalized.",
|
||||
"pr": 1178
|
||||
}
|
||||
],
|
||||
"timestamp": 1541740904
|
||||
},
|
||||
{
|
||||
"version": "3.0.0",
|
||||
"changes": [
|
||||
|
||||
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v3.0.1 - _November 9, 2018_
|
||||
|
||||
* Fix bug in `ForwarderWrapper` where `feeRecipientAddress` was not correctly normalized. (#1178)
|
||||
|
||||
## v3.0.0 - _October 18, 2018_
|
||||
|
||||
* Add optional validation to the forwarder wrapper methods
|
||||
@@ -15,6 +19,8 @@ CHANGELOG
|
||||
* Removed `setProvider` method in top-level `ContractWrapper` class and added new `unsubscribeAll` method. (#1105)
|
||||
* Some properties and methods have been renamed. For example, some methods that previously could throw no longer can, and so their names have been updated accordingly. (#1105)
|
||||
* Removed ContractNotFound errors. Checking for this error was somewhat ineffecient. Relevant methods/functions now return the default error from web3-wrapper, which we feel provides enough information. (#1105)
|
||||
* Add `ForwarderWrapperError` to public interface (#1147)
|
||||
* Add `ContractWrapperError.SignatureRequestDenied` to public interface (#1147)
|
||||
|
||||
## v2.0.2 - _October 4, 2018_
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contract-wrappers",
|
||||
"version": "3.0.0",
|
||||
"version": "3.0.1",
|
||||
"description": "Smart TS wrappers for 0x smart contracts",
|
||||
"keywords": [
|
||||
"0xproject",
|
||||
@@ -13,7 +13,7 @@
|
||||
"scripts": {
|
||||
"build": "tsc -b",
|
||||
"build:ci": "yarn build",
|
||||
"lint": "tslint --project . --exclude **/src/contract_wrappers/**/* --exclude **/lib/**/*",
|
||||
"lint": "tslint --format stylish --project . --exclude **/lib/**/*",
|
||||
"test:circleci": "run-s test:coverage",
|
||||
"test": "yarn run_mocha",
|
||||
"rebuild_and_test": "run-s build test",
|
||||
@@ -37,10 +37,10 @@
|
||||
"node": ">=6.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@0x/dev-utils": "^1.0.13",
|
||||
"@0x/migrations": "^2.0.0",
|
||||
"@0x/subproviders": "^2.1.0",
|
||||
"@0x/tslint-config": "^1.0.9",
|
||||
"@0x/dev-utils": "^1.0.14",
|
||||
"@0x/migrations": "^2.0.1",
|
||||
"@0x/subproviders": "^2.1.1",
|
||||
"@0x/tslint-config": "^1.0.10",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^2.2.42",
|
||||
"@types/node": "*",
|
||||
@@ -65,18 +65,18 @@
|
||||
"web3-provider-engine": "14.0.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/abi-gen-wrappers": "^1.0.1",
|
||||
"@0x/assert": "^1.0.14",
|
||||
"@0x/contract-addresses": "^1.0.1",
|
||||
"@0x/contract-artifacts": "^1.0.1",
|
||||
"@0x/fill-scenarios": "^1.0.8",
|
||||
"@0x/json-schemas": "^2.0.0",
|
||||
"@0x/order-utils": "^2.0.0",
|
||||
"@0x/types": "^1.2.0",
|
||||
"@0x/typescript-typings": "^3.0.3",
|
||||
"@0x/utils": "^2.0.3",
|
||||
"@0x/web3-wrapper": "^3.1.0",
|
||||
"ethereum-types": "^1.1.1",
|
||||
"@0x/abi-gen-wrappers": "^1.0.2",
|
||||
"@0x/assert": "^1.0.15",
|
||||
"@0x/contract-addresses": "^1.1.0",
|
||||
"@0x/contract-artifacts": "^1.1.0",
|
||||
"@0x/fill-scenarios": "^1.0.9",
|
||||
"@0x/json-schemas": "^2.0.1",
|
||||
"@0x/order-utils": "^2.0.1",
|
||||
"@0x/types": "^1.2.1",
|
||||
"@0x/typescript-typings": "^3.0.4",
|
||||
"@0x/utils": "^2.0.4",
|
||||
"@0x/web3-wrapper": "^3.1.1",
|
||||
"ethereum-types": "^1.1.2",
|
||||
"ethereumjs-blockstream": "6.0.0",
|
||||
"ethereumjs-util": "^5.1.1",
|
||||
"ethers": "~4.0.4",
|
||||
|
||||
@@ -28,10 +28,10 @@ export abstract class ContractWrapper {
|
||||
protected _networkId: number;
|
||||
protected _web3Wrapper: Web3Wrapper;
|
||||
private _blockAndLogStreamerIfExists: BlockAndLogStreamer<Block, Log> | undefined;
|
||||
private _blockPollingIntervalMs: number;
|
||||
private readonly _blockPollingIntervalMs: number;
|
||||
private _blockAndLogStreamIntervalIfExists?: NodeJS.Timer;
|
||||
private _filters: { [filterToken: string]: FilterObject };
|
||||
private _filterCallbacks: {
|
||||
private readonly _filters: { [filterToken: string]: FilterObject };
|
||||
private readonly _filterCallbacks: {
|
||||
[filterToken: string]: EventCallback<ContractEventArgs>;
|
||||
};
|
||||
private _onLogAddedSubscriptionToken: string | undefined;
|
||||
|
||||
@@ -34,6 +34,9 @@ export class ERC20ProxyWrapper extends ContractWrapper {
|
||||
*/
|
||||
public async getProxyIdAsync(): Promise<AssetProxyId> {
|
||||
const ERC20ProxyContractInstance = this._getERC20ProxyContract();
|
||||
// Note(albrow): Below is a TSLint false positive. Code won't compile if
|
||||
// you remove the type assertion.
|
||||
/* tslint:disable-next-line:no-unnecessary-type-assertion */
|
||||
const proxyId = (await ERC20ProxyContractInstance.getProxyId.callAsync()) as AssetProxyId;
|
||||
return proxyId;
|
||||
}
|
||||
|
||||
@@ -18,12 +18,11 @@ import {
|
||||
} from '../types';
|
||||
import { assert } from '../utils/assert';
|
||||
import { constants } from '../utils/constants';
|
||||
import { utils } from '../utils/utils';
|
||||
|
||||
import { ContractWrapper } from './contract_wrapper';
|
||||
import { ERC20ProxyWrapper } from './erc20_proxy_wrapper';
|
||||
|
||||
const removeUndefinedProperties = _.pickBy;
|
||||
|
||||
/**
|
||||
* This class includes all the functionality related to interacting with ERC20 token contracts.
|
||||
* All ERC20 method calls are supported, along with some convenience methods for getting/setting allowances
|
||||
@@ -32,8 +31,8 @@ const removeUndefinedProperties = _.pickBy;
|
||||
export class ERC20TokenWrapper extends ContractWrapper {
|
||||
public abi: ContractAbi = ERC20Token.compilerOutput.abi;
|
||||
public UNLIMITED_ALLOWANCE_IN_BASE_UNITS = constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
|
||||
private _tokenContractsByAddress: { [address: string]: ERC20TokenContract };
|
||||
private _erc20ProxyWrapper: ERC20ProxyWrapper;
|
||||
private readonly _tokenContractsByAddress: { [address: string]: ERC20TokenContract };
|
||||
private readonly _erc20ProxyWrapper: ERC20ProxyWrapper;
|
||||
/**
|
||||
* Instantiate ERC20TokenWrapper
|
||||
* @param web3Wrapper Web3Wrapper instance to use
|
||||
@@ -108,7 +107,7 @@ export class ERC20TokenWrapper extends ContractWrapper {
|
||||
const txHash = await tokenContract.approve.sendTransactionAsync(
|
||||
normalizedSpenderAddress,
|
||||
amountInBaseUnits,
|
||||
removeUndefinedProperties({
|
||||
utils.removeUndefinedProperties({
|
||||
from: normalizedOwnerAddress,
|
||||
gas: txOpts.gasLimit,
|
||||
gasPrice: txOpts.gasPrice,
|
||||
@@ -278,7 +277,7 @@ export class ERC20TokenWrapper extends ContractWrapper {
|
||||
const txHash = await tokenContract.transfer.sendTransactionAsync(
|
||||
normalizedToAddress,
|
||||
amountInBaseUnits,
|
||||
removeUndefinedProperties({
|
||||
utils.removeUndefinedProperties({
|
||||
from: normalizedFromAddress,
|
||||
gas: txOpts.gasLimit,
|
||||
gasPrice: txOpts.gasPrice,
|
||||
@@ -339,7 +338,7 @@ export class ERC20TokenWrapper extends ContractWrapper {
|
||||
normalizedFromAddress,
|
||||
normalizedToAddress,
|
||||
amountInBaseUnits,
|
||||
removeUndefinedProperties({
|
||||
utils.removeUndefinedProperties({
|
||||
from: normalizedSenderAddress,
|
||||
gas: txOpts.gasLimit,
|
||||
gasPrice: txOpts.gasPrice,
|
||||
|
||||
@@ -34,6 +34,9 @@ export class ERC721ProxyWrapper extends ContractWrapper {
|
||||
*/
|
||||
public async getProxyIdAsync(): Promise<AssetProxyId> {
|
||||
const ERC721ProxyContractInstance = await this._getERC721ProxyContract();
|
||||
// Note(albrow): Below is a TSLint false positive. Code won't compile if
|
||||
// you remove the type assertion.
|
||||
/* tslint:disable-next-line:no-unnecessary-type-assertion */
|
||||
const proxyId = (await ERC721ProxyContractInstance.getProxyId.callAsync()) as AssetProxyId;
|
||||
return proxyId;
|
||||
}
|
||||
|
||||
@@ -18,12 +18,11 @@ import {
|
||||
} from '../types';
|
||||
import { assert } from '../utils/assert';
|
||||
import { constants } from '../utils/constants';
|
||||
import { utils } from '../utils/utils';
|
||||
|
||||
import { ContractWrapper } from './contract_wrapper';
|
||||
import { ERC721ProxyWrapper } from './erc721_proxy_wrapper';
|
||||
|
||||
const removeUndefinedProperties = _.pickBy;
|
||||
|
||||
/**
|
||||
* This class includes all the functionality related to interacting with ERC721 token contracts.
|
||||
* All ERC721 method calls are supported, along with some convenience methods for getting/setting allowances
|
||||
@@ -31,8 +30,8 @@ const removeUndefinedProperties = _.pickBy;
|
||||
*/
|
||||
export class ERC721TokenWrapper extends ContractWrapper {
|
||||
public abi: ContractAbi = ERC721Token.compilerOutput.abi;
|
||||
private _tokenContractsByAddress: { [address: string]: ERC721TokenContract };
|
||||
private _erc721ProxyWrapper: ERC721ProxyWrapper;
|
||||
private readonly _tokenContractsByAddress: { [address: string]: ERC721TokenContract };
|
||||
private readonly _erc721ProxyWrapper: ERC721ProxyWrapper;
|
||||
/**
|
||||
* Instantiate ERC721TokenWrapper
|
||||
* @param web3Wrapper Web3Wrapper instance to use
|
||||
@@ -235,7 +234,7 @@ export class ERC721TokenWrapper extends ContractWrapper {
|
||||
const txHash = await tokenContract.setApprovalForAll.sendTransactionAsync(
|
||||
normalizedOperatorAddress,
|
||||
isApproved,
|
||||
removeUndefinedProperties({
|
||||
utils.removeUndefinedProperties({
|
||||
gas: txOpts.gasLimit,
|
||||
gasPrice: txOpts.gasPrice,
|
||||
from: normalizedOwnerAddress,
|
||||
@@ -295,7 +294,7 @@ export class ERC721TokenWrapper extends ContractWrapper {
|
||||
const txHash = await tokenContract.approve.sendTransactionAsync(
|
||||
normalizedApprovedAddress,
|
||||
tokenId,
|
||||
removeUndefinedProperties({
|
||||
utils.removeUndefinedProperties({
|
||||
gas: txOpts.gasLimit,
|
||||
gasPrice: txOpts.gasPrice,
|
||||
from: tokenOwnerAddress,
|
||||
@@ -366,7 +365,7 @@ export class ERC721TokenWrapper extends ContractWrapper {
|
||||
ownerAddress,
|
||||
normalizedReceiverAddress,
|
||||
tokenId,
|
||||
removeUndefinedProperties({
|
||||
utils.removeUndefinedProperties({
|
||||
gas: txOpts.gasLimit,
|
||||
gasPrice: txOpts.gasPrice,
|
||||
from: normalizedSenderAddress,
|
||||
|
||||
@@ -8,22 +8,21 @@ import * as _ from 'lodash';
|
||||
|
||||
import { BlockRange, ContractWrappersError, EventCallback, IndexedFilterValues, TransactionOpts } from '../types';
|
||||
import { assert } from '../utils/assert';
|
||||
import { utils } from '../utils/utils';
|
||||
|
||||
import { ContractWrapper } from './contract_wrapper';
|
||||
import { ERC20TokenWrapper } from './erc20_token_wrapper';
|
||||
|
||||
const removeUndefinedProperties = _.pickBy;
|
||||
|
||||
/**
|
||||
* This class includes all the functionality related to interacting with a wrapped Ether ERC20 token contract.
|
||||
* The caller can convert ETH into the equivalent number of wrapped ETH ERC20 tokens and back.
|
||||
*/
|
||||
export class EtherTokenWrapper extends ContractWrapper {
|
||||
public abi: ContractAbi = WETH9.compilerOutput.abi;
|
||||
private _etherTokenContractsByAddress: {
|
||||
private readonly _etherTokenContractsByAddress: {
|
||||
[address: string]: WETH9Contract;
|
||||
} = {};
|
||||
private _erc20TokenWrapper: ERC20TokenWrapper;
|
||||
private readonly _erc20TokenWrapper: ERC20TokenWrapper;
|
||||
/**
|
||||
* Instantiate EtherTokenWrapper.
|
||||
* @param web3Wrapper Web3Wrapper instance to use
|
||||
@@ -67,7 +66,7 @@ export class EtherTokenWrapper extends ContractWrapper {
|
||||
|
||||
const wethContract = await this._getEtherTokenContractAsync(normalizedEtherTokenAddress);
|
||||
const txHash = await wethContract.deposit.sendTransactionAsync(
|
||||
removeUndefinedProperties({
|
||||
utils.removeUndefinedProperties({
|
||||
from: normalizedDepositorAddress,
|
||||
value: amountInWei,
|
||||
gas: txOpts.gasLimit,
|
||||
@@ -109,7 +108,7 @@ export class EtherTokenWrapper extends ContractWrapper {
|
||||
const wethContract = await this._getEtherTokenContractAsync(normalizedEtherTokenAddress);
|
||||
const txHash = await wethContract.withdraw.sendTransactionAsync(
|
||||
amountInWei,
|
||||
removeUndefinedProperties({
|
||||
utils.removeUndefinedProperties({
|
||||
from: normalizedWithdrawerAddress,
|
||||
gas: txOpts.gasLimit,
|
||||
gasPrice: txOpts.gasPrice,
|
||||
|
||||
@@ -46,8 +46,8 @@ export class ExchangeWrapper extends ContractWrapper {
|
||||
public address: string;
|
||||
public zrxTokenAddress: string;
|
||||
private _exchangeContractIfExists?: ExchangeContract;
|
||||
private _erc721TokenWrapper: ERC721TokenWrapper;
|
||||
private _erc20TokenWrapper: ERC20TokenWrapper;
|
||||
private readonly _erc721TokenWrapper: ERC721TokenWrapper;
|
||||
private readonly _erc20TokenWrapper: ERC20TokenWrapper;
|
||||
/**
|
||||
* Instantiate ExchangeWrapper
|
||||
* @param web3Wrapper Web3Wrapper instance to use.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ForwarderContract } from '@0x/abi-gen-wrappers';
|
||||
import { Forwarder } from '@0x/contract-artifacts';
|
||||
import { schemas } from '@0x/json-schemas';
|
||||
import { AssetProxyId, SignedOrder } from '@0x/types';
|
||||
import { SignedOrder } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { ContractAbi } from 'ethereum-types';
|
||||
@@ -118,7 +118,7 @@ export class ForwarderWrapper extends ContractWrapper {
|
||||
optimizedFeeOrders,
|
||||
feeSignatures,
|
||||
formattedFeePercentage,
|
||||
feeRecipientAddress,
|
||||
normalizedFeeRecipientAddress,
|
||||
{
|
||||
value: ethAmount,
|
||||
from: normalizedTakerAddress,
|
||||
@@ -207,7 +207,7 @@ export class ForwarderWrapper extends ContractWrapper {
|
||||
optimizedFeeOrders,
|
||||
feeSignatures,
|
||||
formattedFeePercentage,
|
||||
feeRecipientAddress,
|
||||
normalizedFeeRecipientAddress,
|
||||
{
|
||||
value: ethAmount,
|
||||
from: normalizedTakerAddress,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { constants } from './constants';
|
||||
|
||||
@@ -14,4 +15,7 @@ export const utils = {
|
||||
numberPercentageToEtherTokenAmountPercentage(percentage: number): BigNumber {
|
||||
return Web3Wrapper.toBaseUnitAmount(constants.ONE_AMOUNT, constants.ETHER_TOKEN_DECIMALS).mul(percentage);
|
||||
},
|
||||
removeUndefinedProperties<T extends object>(obj: T): Partial<T> {
|
||||
return _.pickBy(obj);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "contracts",
|
||||
"version": "2.1.50",
|
||||
"version": "2.1.51",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -23,7 +23,7 @@
|
||||
"compile": "sol-compiler --contracts-dir contracts",
|
||||
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
|
||||
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers",
|
||||
"lint": "tslint --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"coverage:report:text": "istanbul report text",
|
||||
"coverage:report:html": "istanbul report html && open coverage/index.html",
|
||||
"profiler:report:html": "istanbul report html && open coverage/index.html",
|
||||
@@ -45,12 +45,12 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/packages/contracts/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^1.0.14",
|
||||
"@0x/dev-utils": "^1.0.13",
|
||||
"@0x/sol-compiler": "^1.1.8",
|
||||
"@0x/sol-cov": "^2.1.8",
|
||||
"@0x/subproviders": "^2.1.0",
|
||||
"@0x/tslint-config": "^1.0.9",
|
||||
"@0x/abi-gen": "^1.0.15",
|
||||
"@0x/dev-utils": "^1.0.14",
|
||||
"@0x/sol-compiler": "^1.1.9",
|
||||
"@0x/sol-cov": "^2.1.9",
|
||||
"@0x/subproviders": "^2.1.1",
|
||||
"@0x/tslint-config": "^1.0.10",
|
||||
"@types/bn.js": "^4.11.0",
|
||||
"@types/ethereumjs-abi": "^0.6.0",
|
||||
"@types/lodash": "4.14.104",
|
||||
@@ -71,15 +71,15 @@
|
||||
"yargs": "^10.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^3.0.2",
|
||||
"@0x/order-utils": "^2.0.0",
|
||||
"@0x/types": "^1.2.0",
|
||||
"@0x/typescript-typings": "^3.0.3",
|
||||
"@0x/utils": "^2.0.3",
|
||||
"@0x/web3-wrapper": "^3.1.0",
|
||||
"@0x/base-contract": "^3.0.3",
|
||||
"@0x/order-utils": "^2.0.1",
|
||||
"@0x/types": "^1.2.1",
|
||||
"@0x/typescript-typings": "^3.0.4",
|
||||
"@0x/utils": "^2.0.4",
|
||||
"@0x/web3-wrapper": "^3.1.1",
|
||||
"@types/js-combinatorics": "^0.5.29",
|
||||
"bn.js": "^4.11.8",
|
||||
"ethereum-types": "^1.1.1",
|
||||
"ethereum-types": "^1.1.2",
|
||||
"ethereumjs-abi": "0.6.5",
|
||||
"ethereumjs-util": "^5.1.1",
|
||||
"ethers": "~4.0.4",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/dev-tools-pages",
|
||||
"version": "0.0.2",
|
||||
"version": "0.0.3",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -11,12 +11,12 @@
|
||||
"build:ci": "yarn build",
|
||||
"build:dev": "../../node_modules/.bin/webpack --mode development",
|
||||
"clean": "shx rm -f public/bundle*",
|
||||
"lint": "tslint --project . 'ts/**/*.ts' 'ts/**/*.tsx'",
|
||||
"lint": "tslint --format stylish --project . 'ts/**/*.ts' 'ts/**/*.tsx'",
|
||||
"dev": "webpack-dev-server --mode development --content-base public"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@0x/react-shared": "^1.0.17",
|
||||
"@0x/react-shared": "^1.0.18",
|
||||
"basscss": "^8.0.3",
|
||||
"bowser": "^1.9.3",
|
||||
"less": "^2.7.2",
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "1.0.14",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1541740904
|
||||
},
|
||||
{
|
||||
"version": "1.0.13",
|
||||
"changes": [
|
||||
|
||||
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.0.14 - _November 9, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.13 - _October 18, 2018_
|
||||
|
||||
* Make web3-provider-engine types a 'dependency' so it's available to users of the library (#1105)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/dev-utils",
|
||||
"version": "1.0.13",
|
||||
"version": "1.0.14",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -17,7 +17,7 @@
|
||||
"test:coverage": "nyc npm run test --all && yarn coverage:report:lcov",
|
||||
"coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info",
|
||||
"clean": "shx rm -rf lib",
|
||||
"lint": "tslint --project ."
|
||||
"lint": "tslint --format stylish --project ."
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
@@ -29,7 +29,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/packages/dev-utils/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/tslint-config": "^1.0.9",
|
||||
"@0x/tslint-config": "^1.0.10",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^2.2.42",
|
||||
"make-promises-safe": "^1.1.0",
|
||||
@@ -41,14 +41,14 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/subproviders": "^2.1.0",
|
||||
"@0x/types": "^1.2.0",
|
||||
"@0x/typescript-typings": "^3.0.3",
|
||||
"@0x/utils": "^2.0.3",
|
||||
"@0x/web3-wrapper": "^3.1.0",
|
||||
"@0x/subproviders": "^2.1.1",
|
||||
"@0x/types": "^1.2.1",
|
||||
"@0x/typescript-typings": "^3.0.4",
|
||||
"@0x/utils": "^2.0.4",
|
||||
"@0x/web3-wrapper": "^3.1.1",
|
||||
"@types/web3-provider-engine": "^14.0.0",
|
||||
"chai": "^4.0.1",
|
||||
"ethereum-types": "^1.1.1",
|
||||
"ethereum-types": "^1.1.2",
|
||||
"lodash": "^4.17.5"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "1.1.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1541740904
|
||||
},
|
||||
{
|
||||
"version": "1.1.1",
|
||||
"changes": [
|
||||
|
||||
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.1.2 - _November 9, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.1.1 - _October 18, 2018_
|
||||
|
||||
* Add `JSONRPCResponseError` and error field on `JSONRPCResponsePayload`. (#1102)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ethereum-types",
|
||||
"version": "1.1.1",
|
||||
"version": "1.1.2",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -11,7 +11,7 @@
|
||||
"build": "tsc -b",
|
||||
"build:ci": "yarn build",
|
||||
"clean": "shx rm -rf lib generated_docs",
|
||||
"lint": "tslint --project .",
|
||||
"lint": "tslint --format stylish --project .",
|
||||
"docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
|
||||
},
|
||||
"config": {
|
||||
@@ -29,7 +29,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/packages/ethereum-types/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/tslint-config": "^1.0.9",
|
||||
"@0x/tslint-config": "^1.0.10",
|
||||
"make-promises-safe": "^1.1.0",
|
||||
"shx": "^0.2.2",
|
||||
"tslint": "5.11.0",
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "1.0.9",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1541740904
|
||||
},
|
||||
{
|
||||
"version": "1.0.8",
|
||||
"changes": [
|
||||
|
||||
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.0.9 - _November 9, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.8 - _October 18, 2018_
|
||||
|
||||
* Updated to use new @0xproject/contract-artifacts and @0xproject/abi-gen-wrappers packages (#1105)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/fill-scenarios",
|
||||
"version": "1.0.8",
|
||||
"version": "1.0.9",
|
||||
"description": "0x order fill scenario generator",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
@@ -8,7 +8,7 @@
|
||||
"build": "yarn tsc -b",
|
||||
"build:ci": "yarn build",
|
||||
"clean": "shx rm -rf lib src/generated_contract_wrappers",
|
||||
"lint": "tslint --project ."
|
||||
"lint": "tslint --format stylish --project ."
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
@@ -20,7 +20,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/packages/fill-scenarios/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/tslint-config": "^1.0.9",
|
||||
"@0x/tslint-config": "^1.0.10",
|
||||
"@types/lodash": "4.14.104",
|
||||
"make-promises-safe": "^1.1.0",
|
||||
"shx": "^0.2.2",
|
||||
@@ -28,15 +28,15 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/abi-gen-wrappers": "^1.0.1",
|
||||
"@0x/base-contract": "^3.0.2",
|
||||
"@0x/contract-artifacts": "^1.0.1",
|
||||
"@0x/order-utils": "^2.0.0",
|
||||
"@0x/types": "^1.2.0",
|
||||
"@0x/typescript-typings": "^3.0.3",
|
||||
"@0x/utils": "^2.0.3",
|
||||
"@0x/web3-wrapper": "^3.1.0",
|
||||
"ethereum-types": "^1.1.1",
|
||||
"@0x/abi-gen-wrappers": "^1.0.2",
|
||||
"@0x/base-contract": "^3.0.3",
|
||||
"@0x/contract-artifacts": "^1.1.0",
|
||||
"@0x/order-utils": "^2.0.1",
|
||||
"@0x/types": "^1.2.1",
|
||||
"@0x/typescript-typings": "^3.0.4",
|
||||
"@0x/utils": "^2.0.4",
|
||||
"@0x/web3-wrapper": "^3.1.1",
|
||||
"ethereum-types": "^1.1.2",
|
||||
"ethers": "~4.0.4",
|
||||
"lodash": "^4.17.5"
|
||||
},
|
||||
|
||||
13
packages/instant/.staging.discharge.json
Normal file
13
packages/instant/.staging.discharge.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"domain": "0x-instant-staging",
|
||||
"build_command": "yarn build:umd:prod",
|
||||
"upload_directory": "public",
|
||||
"index_key": "index.html",
|
||||
"error_key": "index.html",
|
||||
"trailing_slashes": true,
|
||||
"cache": 3600,
|
||||
"aws_profile": "default",
|
||||
"aws_region": "us-east-1",
|
||||
"cdn": false,
|
||||
"dns_configured": true
|
||||
}
|
||||
@@ -53,7 +53,15 @@ You can deploy a work-in-progress version of 0x Instant at http://0x-instant-dog
|
||||
To build and deploy the site run
|
||||
|
||||
```
|
||||
yarn deploy
|
||||
yarn deploy_dogfood
|
||||
```
|
||||
|
||||
We also have a staging bucket that is to be updated less frequently can be used to share instant externally: http://0x-instant-staging.s3-website-us-east-1.amazonaws.com/
|
||||
|
||||
To build and deploy to this bucket, run
|
||||
|
||||
```
|
||||
yarn deploy_staging
|
||||
```
|
||||
|
||||
**NOTE: On deploying the site, it will say the site is available at a non-existent URL. Please ignore and use the (now updated) URL above.**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/instant",
|
||||
"version": "0.0.3",
|
||||
"version": "0.0.4",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -16,13 +16,14 @@
|
||||
"build:ci": "yarn build",
|
||||
"watch_without_deps": "tsc -w",
|
||||
"dev": "webpack-dev-server --mode development",
|
||||
"lint": "tslint --project .",
|
||||
"lint": "tslint --format stylish --project .",
|
||||
"test": "jest",
|
||||
"test:coverage": "jest --coverage",
|
||||
"rebuild_and_test": "run-s clean build test",
|
||||
"test:circleci": "yarn test:coverage",
|
||||
"clean": "shx rm -rf lib coverage scripts",
|
||||
"deploy": "discharge deploy",
|
||||
"deploy_dogfood": "discharge deploy -c .dogfood.discharge.json",
|
||||
"deploy_staging": "discharge deploy -c .staging.discharge.json",
|
||||
"manual:postpublish": "yarn build; node ./scripts/postpublish.js"
|
||||
},
|
||||
"config": {
|
||||
@@ -44,13 +45,17 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/packages/instant/README.md",
|
||||
"dependencies": {
|
||||
"@0x/asset-buyer": "^2.1.0",
|
||||
"@0x/order-utils": "^2.0.0",
|
||||
"@0x/types": "^1.2.0",
|
||||
"@0x/typescript-typings": "^3.0.3",
|
||||
"@0x/utils": "^2.0.3",
|
||||
"@0x/web3-wrapper": "^3.1.0",
|
||||
"ethereum-types": "^1.1.1",
|
||||
"@0x/assert": "^1.0.15",
|
||||
"@0x/asset-buyer": "^2.2.0",
|
||||
"@0x/json-schemas": "^2.0.1",
|
||||
"@0x/order-utils": "^2.0.1",
|
||||
"@0x/subproviders": "^2.1.1",
|
||||
"@0x/types": "^1.2.1",
|
||||
"@0x/typescript-typings": "^3.0.4",
|
||||
"@0x/utils": "^2.0.4",
|
||||
"@0x/web3-wrapper": "^3.1.1",
|
||||
"copy-to-clipboard": "^3.0.8",
|
||||
"ethereum-types": "^1.1.2",
|
||||
"lodash": "^4.17.10",
|
||||
"polished": "^2.2.0",
|
||||
"react": "^16.5.2",
|
||||
@@ -58,12 +63,12 @@
|
||||
"react-redux": "^5.0.7",
|
||||
"redux": "^4.0.0",
|
||||
"redux-devtools-extension": "^2.13.5",
|
||||
"styled-components": "^3.4.9",
|
||||
"styled-components": "^4.0.2",
|
||||
"ts-optchain": "^0.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@0x/tslint-config": "^1.0.9",
|
||||
"@static/discharge": "^1.2.2",
|
||||
"@0x/tslint-config": "^1.0.10",
|
||||
"@static/discharge": "https://github.com/0xProject/discharge.git",
|
||||
"@types/enzyme": "^3.1.14",
|
||||
"@types/enzyme-adapter-react-16": "^1.0.3",
|
||||
"@types/jest": "^23.3.5",
|
||||
@@ -73,6 +78,7 @@
|
||||
"@types/react-dom": "^16.0.8",
|
||||
"@types/react-redux": "^6.0.9",
|
||||
"@types/redux": "^3.6.0",
|
||||
"@types/styled-components": "^4.0.1",
|
||||
"awesome-typescript-loader": "^5.2.1",
|
||||
"enzyme": "^3.6.0",
|
||||
"enzyme-adapter-react-16": "^1.5.0",
|
||||
|
||||
25
packages/instant/public/external.css
Normal file
25
packages/instant/public/external.css
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
CSS file meant to represent an external (integrators) stylesheet and
|
||||
help ensure that instant looks consistent across environments.
|
||||
*/
|
||||
|
||||
button {
|
||||
font-size: 50px;
|
||||
height: 200px;
|
||||
background-color: red;
|
||||
}
|
||||
|
||||
input {
|
||||
padding: 100px;
|
||||
font-size: 50px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
div {
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
p {
|
||||
background-color: green;
|
||||
margin: 10px;
|
||||
}
|
||||
@@ -5,7 +5,10 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>0x Instant Dev Environment</title>
|
||||
<link rel="stylesheet" href="/external.css">
|
||||
<script type="text/javascript" src="/main.bundle.js" charset="utf-8"></script>
|
||||
<script type="text/javascript" src="https://unpkg.com/jsuri@1.3.1/Uri.js" charset="utf-8"></script>
|
||||
<script type="text/javascript" src="https://unpkg.com/bignumber.js@4.1.0/bignumber.js" charset="utf-8"></script>
|
||||
<style>
|
||||
#zeroExInstantContainer {
|
||||
display: flex;
|
||||
@@ -24,10 +27,110 @@
|
||||
<body>
|
||||
<div id="zeroExInstantContainer"></div>
|
||||
<script>
|
||||
zeroExInstant.render({
|
||||
liquiditySource: 'https://api.radarrelay.com/0x/v2/',
|
||||
assetData: '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498',
|
||||
const removeUndefined = (obj) => {
|
||||
for (let k in obj) if (obj[k] === undefined) delete obj[k];
|
||||
return obj;
|
||||
}
|
||||
BigNumber.config({
|
||||
EXPONENTIAL_AT: 1000,
|
||||
DECIMAL_PLACES: 78,
|
||||
});
|
||||
const providedOrders = [
|
||||
// Order selling REP
|
||||
{
|
||||
senderAddress: '0x0000000000000000000000000000000000000000',
|
||||
makerAddress: '0x34a745008a643eebc58920eaa29fb1165b4a288e',
|
||||
takerAddress: '0x0000000000000000000000000000000000000000',
|
||||
makerFee: new BigNumber('0'),
|
||||
takerFee: new BigNumber('0'),
|
||||
makerAssetAmount: new BigNumber('200000000000000000000'),
|
||||
takerAssetAmount: new BigNumber('10000000000000000000'),
|
||||
makerAssetData: '0xf47261b00000000000000000000000008cb3971b8eb709c14616bd556ff6683019e90d9c',
|
||||
takerAssetData: '0xf47261b0000000000000000000000000d0a1e359811322d97991e03f863a0c30c2cf029c',
|
||||
expirationTimeSeconds: new BigNumber('1601535600'),
|
||||
feeRecipientAddress: '0x0000000000000000000000000000000000000000',
|
||||
salt: new BigNumber('3101985707338942582579795423923841749956600670712030922928319824580764688653'),
|
||||
signature: '0x1bd4d5686fea801fe33c68c4944356085e7e6cb553eb7073160abd815609f714e85fb47f44b7ffd0a2a1321ac40d72d55163869d0a50fdb5a402132150fe33a08403',
|
||||
exchangeAddress: '0x35dd2932454449b14cee11a94d3674a936d5d7b2'
|
||||
},
|
||||
// Order selling ZRX
|
||||
{
|
||||
senderAddress: '0x0000000000000000000000000000000000000000',
|
||||
makerAddress: '0x34a745008a643eebc58920eaa29fb1165b4a288e',
|
||||
takerAddress: '0x0000000000000000000000000000000000000000',
|
||||
makerFee: new BigNumber('0'),
|
||||
takerFee: new BigNumber('0'),
|
||||
makerAssetAmount: new BigNumber('300000000000000000000'),
|
||||
takerAssetAmount: new BigNumber('31000000000000000000'),
|
||||
makerAssetData: '0xf47261b00000000000000000000000002002d3812f58e35f0ea1ffbf80a75a38c32175fa',
|
||||
takerAssetData: '0xf47261b0000000000000000000000000d0a1e359811322d97991e03f863a0c30c2cf029c',
|
||||
expirationTimeSeconds: new BigNumber('2524636800'),
|
||||
feeRecipientAddress: '0x0000000000000000000000000000000000000000',
|
||||
salt: new BigNumber('64592004666704945574675477805199411288137454783320798602050822322450089238268'),
|
||||
signature: '0x1c13cacddca8d7d8248e91f412377e68f8f1f9891a59a6c1b2eea9f7b33558c30c4fb86a448e08ab7def40a28fb3a3062dcb33bb3c45302447fce5c4288b7c7f5b03',
|
||||
exchangeAddress: '0x35dd2932454449b14cee11a94d3674a936d5d7b2'
|
||||
},
|
||||
// Order selling GNT
|
||||
{
|
||||
senderAddress: '0x0000000000000000000000000000000000000000',
|
||||
makerAddress: '0x34a745008a643eebc58920eaa29fb1165b4a288e',
|
||||
takerAddress: '0x0000000000000000000000000000000000000000',
|
||||
makerFee: new BigNumber('0'),
|
||||
takerFee: new BigNumber('0'),
|
||||
makerAssetAmount: new BigNumber('250000000000000000000'),
|
||||
takerAssetAmount: new BigNumber('10000000000000000000'),
|
||||
makerAssetData: '0xf47261b000000000000000000000000031fb614e223706f15d0d3c5f4b08bdf0d5c78623',
|
||||
takerAssetData: '0xf47261b0000000000000000000000000d0a1e359811322d97991e03f863a0c30c2cf029c',
|
||||
expirationTimeSeconds: new BigNumber('1601535600'),
|
||||
feeRecipientAddress: '0x0000000000000000000000000000000000000000',
|
||||
salt: new BigNumber('40204378562212615907903051460421336779451270522691667164301816101569427926606'),
|
||||
signature: '0x1c788bf4b93769da1e8f195f52f0f59b4a298ac6da30cf6d05a87ed4be5ee974f61352ed1bc6a0844d0962b8c894c9ca08e452431255958a4e98dd93cbe1fbc73803',
|
||||
exchangeAddress: '0x35dd2932454449b14cee11a94d3674a936d5d7b2'
|
||||
},
|
||||
// Order selling MKR
|
||||
{
|
||||
senderAddress: '0x0000000000000000000000000000000000000000',
|
||||
makerAddress: '0x34a745008a643eebc58920eaa29fb1165b4a288e',
|
||||
takerAddress: '0x0000000000000000000000000000000000000000',
|
||||
makerFee: new BigNumber('0'),
|
||||
takerFee: new BigNumber('0'),
|
||||
makerAssetAmount: new BigNumber('200000000000000000000'),
|
||||
takerAssetAmount: new BigNumber('5000000000000000000'),
|
||||
makerAssetData: '0xf47261b00000000000000000000000007b6b10caa9e8e9552ba72638ea5b47c25afea1f3',
|
||||
takerAssetData: '0xf47261b0000000000000000000000000d0a1e359811322d97991e03f863a0c30c2cf029c',
|
||||
expirationTimeSeconds: new BigNumber('1601535600'),
|
||||
feeRecipientAddress: '0x0000000000000000000000000000000000000000',
|
||||
salt: new BigNumber('71338269924068280039932133924198049371838034090153601678083172009862985793828'),
|
||||
signature: '0x1bb3151d57ee1e8fa697767ce83ee4ba77d1ceb8cc1e79c7d77126b3687517704c50c6b3d9cb42c7e7d4478d574b297dfbd1626c5c18a7bc9c2a792c4c07f0797c03',
|
||||
exchangeAddress: '0x35dd2932454449b14cee11a94d3674a936d5d7b2'
|
||||
}
|
||||
];
|
||||
const queryParams = new Uri(window.location.search);
|
||||
const renderOptionsDefaults = {
|
||||
orderSource: 'https://api.radarrelay.com/0x/v2/',
|
||||
onClose: () => { console.log('0x Instant Closed') }
|
||||
}
|
||||
const orderSourceOverride = queryParams.getQueryParamValue('orderSource');
|
||||
const availableAssetDatasString = queryParams.getQueryParamValue('availableAssetDatas');
|
||||
const feeRecipientOverride = queryParams.getQueryParamValue('feeRecipient');
|
||||
const feePercentageOverride = +queryParams.getQueryParamValue('feePercentage');
|
||||
let affiliateInfoOverride;
|
||||
if (feeRecipientOverride !== undefined && feePercentageOverride !== undefined) {
|
||||
affiliateInfoOverride = {
|
||||
feeRecipient: feeRecipientOverride,
|
||||
feePercentage: feePercentageOverride
|
||||
};
|
||||
}
|
||||
const renderOptionsOverrides = {
|
||||
orderSource: orderSourceOverride === 'provided' ? providedOrders : orderSourceOverride,
|
||||
networkId: +queryParams.getQueryParamValue('networkId') || undefined,
|
||||
defaultAssetBuyAmount: +queryParams.getQueryParamValue('defaultAssetBuyAmount') || undefined,
|
||||
availableAssetDatas: availableAssetDatasString ? JSON.parse(availableAssetDatasString) : undefined,
|
||||
defaultSelectedAssetData: queryParams.getQueryParamValue('defaultSelectedAssetData'),
|
||||
affiliateInfo: affiliateInfoOverride,
|
||||
}
|
||||
const renderOptions = Object.assign({}, renderOptionsDefaults, removeUndefined(renderOptionsOverrides));
|
||||
zeroExInstant.render(renderOptions);
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ColorOption } from '../style/theme';
|
||||
import { util } from '../util/util';
|
||||
|
||||
import { Container, Input } from './ui';
|
||||
|
||||
export interface AmountInputProps {
|
||||
fontColor?: ColorOption;
|
||||
fontSize?: string;
|
||||
value?: BigNumber;
|
||||
onChange: (value?: BigNumber) => void;
|
||||
}
|
||||
|
||||
export class AmountInput extends React.Component<AmountInputProps> {
|
||||
public static defaultProps = {
|
||||
onChange: util.boundNoop,
|
||||
};
|
||||
public render(): React.ReactNode {
|
||||
const { fontColor, fontSize, value } = this.props;
|
||||
return (
|
||||
<Container borderBottom="1px solid rgba(255,255,255,0.3)" display="inline-block">
|
||||
<Input
|
||||
fontColor={fontColor}
|
||||
fontSize={fontSize}
|
||||
onChange={this._handleChange}
|
||||
value={!_.isUndefined(value) ? value.toString() : ''}
|
||||
placeholder="0.00"
|
||||
width="2.2em"
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
private readonly _handleChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
const value = event.target.value;
|
||||
let bigNumberValue;
|
||||
if (!_.isEmpty(value)) {
|
||||
try {
|
||||
bigNumberValue = new BigNumber(event.target.value);
|
||||
} catch {
|
||||
// We don't want to allow values that can't be a BigNumber, so don't even call onChange.
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.props.onChange(bigNumberValue);
|
||||
};
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import { ColorOption } from '../style/theme';
|
||||
|
||||
import { Pulse } from './animations/pulse';
|
||||
|
||||
import { Text } from './ui';
|
||||
import { Text } from './ui/text';
|
||||
|
||||
interface PlainPlaceholder {
|
||||
color: ColorOption;
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
import { InterpolationValue } from 'styled-components';
|
||||
|
||||
import { media, OptionallyScreenSpecific, stylesForMedia } from '../../style/media';
|
||||
import { css, keyframes, styled } from '../../style/theme';
|
||||
|
||||
export interface TransitionInfo {
|
||||
from: string;
|
||||
to: string;
|
||||
}
|
||||
|
||||
const generateTransitionInfoCss = (
|
||||
key: keyof TransitionInfo,
|
||||
top?: TransitionInfo,
|
||||
bottom?: TransitionInfo,
|
||||
left?: TransitionInfo,
|
||||
right?: TransitionInfo,
|
||||
): string => {
|
||||
const topStringIfExists = top ? `top: ${top[key]};` : '';
|
||||
const bottomStringIfExists = bottom ? `bottom: ${bottom[key]};` : '';
|
||||
const leftStringIfExists = left ? `left: ${left[key]};` : '';
|
||||
const rightStringIfExists = right ? `right: ${right[key]};` : '';
|
||||
return `
|
||||
${topStringIfExists}
|
||||
${bottomStringIfExists}
|
||||
${leftStringIfExists}
|
||||
${rightStringIfExists}
|
||||
`;
|
||||
};
|
||||
|
||||
const slideKeyframeGenerator = (
|
||||
position: string,
|
||||
top?: TransitionInfo,
|
||||
bottom?: TransitionInfo,
|
||||
left?: TransitionInfo,
|
||||
right?: TransitionInfo,
|
||||
) => keyframes`
|
||||
from {
|
||||
position: ${position};
|
||||
${generateTransitionInfoCss('from', top, bottom, left, right)}
|
||||
}
|
||||
|
||||
to {
|
||||
position: ${position};
|
||||
${generateTransitionInfoCss('to', top, bottom, left, right)}
|
||||
}
|
||||
`;
|
||||
|
||||
export interface PositionAnimationSettings {
|
||||
top?: TransitionInfo;
|
||||
bottom?: TransitionInfo;
|
||||
left?: TransitionInfo;
|
||||
right?: TransitionInfo;
|
||||
timingFunction: string;
|
||||
duration?: string;
|
||||
position?: string;
|
||||
}
|
||||
|
||||
const generatePositionAnimationCss = (positionSettings: PositionAnimationSettings) => {
|
||||
return css`
|
||||
animation-name: ${slideKeyframeGenerator(
|
||||
positionSettings.position || 'relative',
|
||||
positionSettings.top,
|
||||
positionSettings.bottom,
|
||||
positionSettings.left,
|
||||
positionSettings.right,
|
||||
)};
|
||||
animation-duration: ${positionSettings.duration || '0.3s'};
|
||||
animation-timing-function: ${positionSettings.timingFunction};
|
||||
animation-delay: 0s;
|
||||
animation-iteration-count: 1;
|
||||
animation-fill-mode: forwards;
|
||||
position: ${positionSettings.position || 'relative'};
|
||||
width: 100%;
|
||||
`;
|
||||
};
|
||||
|
||||
export interface PositionAnimationProps {
|
||||
positionSettings: OptionallyScreenSpecific<PositionAnimationSettings>;
|
||||
zIndex?: OptionallyScreenSpecific<number>;
|
||||
height?: string;
|
||||
}
|
||||
|
||||
const defaultAnimation = (positionSettings: OptionallyScreenSpecific<PositionAnimationSettings>) => {
|
||||
const bestDefault = 'default' in positionSettings ? positionSettings.default : positionSettings;
|
||||
return generatePositionAnimationCss(bestDefault);
|
||||
};
|
||||
const animationForSize = (
|
||||
positionSettings: OptionallyScreenSpecific<PositionAnimationSettings>,
|
||||
sizeKey: 'sm' | 'md' | 'lg',
|
||||
mediaFn: (...args: any[]) => InterpolationValue[],
|
||||
) => {
|
||||
// checking default makes sure we have a PositionAnimationSettings object
|
||||
// and then we check to see if we have a setting for the specific `sizeKey`
|
||||
const animationSettingsForSize = 'default' in positionSettings && positionSettings[sizeKey];
|
||||
return animationSettingsForSize && mediaFn`${generatePositionAnimationCss(animationSettingsForSize)}`;
|
||||
};
|
||||
|
||||
export const PositionAnimation =
|
||||
styled.div <
|
||||
PositionAnimationProps >
|
||||
`
|
||||
&& {
|
||||
${props => props.zIndex && stylesForMedia<number>('z-index', props.zIndex)}
|
||||
${props => defaultAnimation(props.positionSettings)}
|
||||
${props => animationForSize(props.positionSettings, 'sm', media.small)}
|
||||
${props => animationForSize(props.positionSettings, 'md', media.medium)}
|
||||
${props => animationForSize(props.positionSettings, 'lg', media.large)}
|
||||
${props => (props.height ? `height: ${props.height};` : '')}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,26 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { OptionallyScreenSpecific } from '../../style/media';
|
||||
|
||||
import { PositionAnimation, PositionAnimationSettings } from './position_animation';
|
||||
|
||||
export type SlideAnimationState = 'slidIn' | 'slidOut' | 'none';
|
||||
export interface SlideAnimationProps {
|
||||
animationState: SlideAnimationState;
|
||||
slideInSettings: OptionallyScreenSpecific<PositionAnimationSettings>;
|
||||
slideOutSettings: OptionallyScreenSpecific<PositionAnimationSettings>;
|
||||
zIndex?: OptionallyScreenSpecific<number>;
|
||||
height?: string;
|
||||
}
|
||||
|
||||
export const SlideAnimation: React.StatelessComponent<SlideAnimationProps> = props => {
|
||||
if (props.animationState === 'none') {
|
||||
return <React.Fragment>{props.children}</React.Fragment>;
|
||||
}
|
||||
const positionSettings = props.animationState === 'slidIn' ? props.slideInSettings : props.slideOutSettings;
|
||||
return (
|
||||
<PositionAnimation height={props.height} positionSettings={positionSettings} zIndex={props.zIndex}>
|
||||
{props.children}
|
||||
</PositionAnimation>
|
||||
);
|
||||
};
|
||||
@@ -1,54 +0,0 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { keyframes, styled } from '../../style/theme';
|
||||
|
||||
const slideKeyframeGenerator = (fromY: string, toY: string) => keyframes`
|
||||
from {
|
||||
position: relative;
|
||||
top: ${fromY};
|
||||
}
|
||||
|
||||
to {
|
||||
position: relative;
|
||||
top: ${toY};
|
||||
}
|
||||
`;
|
||||
|
||||
export interface SlideAnimationProps {
|
||||
keyframes: string;
|
||||
animationType: string;
|
||||
animationDirection?: string;
|
||||
}
|
||||
|
||||
export const SlideAnimation =
|
||||
styled.div <
|
||||
SlideAnimationProps >
|
||||
`
|
||||
animation-name: ${props => props.keyframes};
|
||||
animation-duration: 0.3s;
|
||||
animation-timing-function: ${props => props.animationType};
|
||||
animation-delay: 0s;
|
||||
animation-iteration-count: 1;
|
||||
animation-fill-mode: ${props => props.animationDirection || 'none'};
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
export interface SlideAnimationComponentProps {
|
||||
downY: string;
|
||||
}
|
||||
|
||||
export const SlideUpAnimation: React.StatelessComponent<SlideAnimationComponentProps> = props => (
|
||||
<SlideAnimation animationType="ease-in" keyframes={slideKeyframeGenerator(props.downY, '0px')}>
|
||||
{props.children}
|
||||
</SlideAnimation>
|
||||
);
|
||||
|
||||
export const SlideDownAnimation: React.StatelessComponent<SlideAnimationComponentProps> = props => (
|
||||
<SlideAnimation
|
||||
animationDirection="forwards"
|
||||
animationType="cubic-bezier(0.25, 0.1, 0.25, 1)"
|
||||
keyframes={slideKeyframeGenerator('0px', props.downY)}
|
||||
>
|
||||
{props.children}
|
||||
</SlideAnimation>
|
||||
);
|
||||
@@ -1,39 +0,0 @@
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ColorOption } from '../style/theme';
|
||||
import { ERC20Asset } from '../types';
|
||||
import { assetUtils } from '../util/asset';
|
||||
import { util } from '../util/util';
|
||||
|
||||
import { AmountInput, AmountInputProps } from './amount_input';
|
||||
import { Container, Text } from './ui';
|
||||
|
||||
// Asset amounts only apply to ERC20 assets
|
||||
export interface AssetAmountInputProps extends AmountInputProps {
|
||||
asset?: ERC20Asset;
|
||||
onChange: (value?: BigNumber, asset?: ERC20Asset) => void;
|
||||
}
|
||||
|
||||
export class AssetAmountInput extends React.Component<AssetAmountInputProps> {
|
||||
public static defaultProps = {
|
||||
onChange: util.boundNoop,
|
||||
};
|
||||
public render(): React.ReactNode {
|
||||
const { asset, onChange, ...rest } = this.props;
|
||||
return (
|
||||
<Container>
|
||||
<AmountInput {...rest} onChange={this._handleChange} />
|
||||
<Container display="inline-block" marginLeft="10px">
|
||||
<Text fontSize={rest.fontSize} fontColor={ColorOption.white} textTransform="uppercase">
|
||||
{assetUtils.bestNameForAsset(asset)}
|
||||
</Text>
|
||||
</Container>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
private readonly _handleChange = (value?: BigNumber): void => {
|
||||
this.props.onChange(value, this.props.asset);
|
||||
};
|
||||
}
|
||||
@@ -1,20 +1,29 @@
|
||||
import { AssetBuyer, AssetBuyerError, BuyQuote } from '@0x/asset-buyer';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import * as _ from 'lodash';
|
||||
import * as React from 'react';
|
||||
import { oc } from 'ts-optchain';
|
||||
|
||||
import { WEB_3_WRAPPER_TRANSACTION_FAILED_ERROR_MSG_PREFIX } from '../constants';
|
||||
import { ColorOption } from '../style/theme';
|
||||
import { AffiliateInfo, ZeroExInstantError } from '../types';
|
||||
import { gasPriceEstimator } from '../util/gas_price_estimator';
|
||||
import { util } from '../util/util';
|
||||
import { web3Wrapper } from '../util/web3_wrapper';
|
||||
|
||||
import { Button, Text } from './ui';
|
||||
import { Button } from './ui/button';
|
||||
|
||||
export interface BuyButtonProps {
|
||||
accountAddress?: string;
|
||||
accountEthBalanceInWei?: BigNumber;
|
||||
buyQuote?: BuyQuote;
|
||||
assetBuyer?: AssetBuyer;
|
||||
onAwaitingSignature: (buyQuote: BuyQuote) => void;
|
||||
onSignatureDenied: (buyQuote: BuyQuote, preventedError: Error) => void;
|
||||
onBuyProcessing: (buyQuote: BuyQuote, txHash: string) => void;
|
||||
assetBuyer: AssetBuyer;
|
||||
web3Wrapper: Web3Wrapper;
|
||||
affiliateInfo?: AffiliateInfo;
|
||||
onValidationPending: (buyQuote: BuyQuote) => void;
|
||||
onValidationFail: (buyQuote: BuyQuote, errorMessage: AssetBuyerError | ZeroExInstantError) => void;
|
||||
onSignatureDenied: (buyQuote: BuyQuote) => void;
|
||||
onBuyProcessing: (buyQuote: BuyQuote, txHash: string, startTimeUnix: number, expectedEndTimeUnix: number) => void;
|
||||
onBuySuccess: (buyQuote: BuyQuote, txHash: string) => void;
|
||||
onBuyFailure: (buyQuote: BuyQuote, txHash: string) => void;
|
||||
}
|
||||
@@ -26,35 +35,58 @@ export class BuyButton extends React.Component<BuyButtonProps> {
|
||||
onBuyFailure: util.boundNoop,
|
||||
};
|
||||
public render(): React.ReactNode {
|
||||
const shouldDisableButton = _.isUndefined(this.props.buyQuote) || _.isUndefined(this.props.assetBuyer);
|
||||
const { buyQuote, accountAddress } = this.props;
|
||||
const shouldDisableButton = _.isUndefined(buyQuote) || _.isUndefined(accountAddress);
|
||||
return (
|
||||
<Button width="100%" onClick={this._handleClick} isDisabled={shouldDisableButton}>
|
||||
<Text fontColor={ColorOption.white} fontWeight={600} fontSize="20px">
|
||||
Buy
|
||||
</Text>
|
||||
<Button
|
||||
width="100%"
|
||||
onClick={this._handleClick}
|
||||
isDisabled={shouldDisableButton}
|
||||
fontColor={ColorOption.white}
|
||||
fontSize="20px"
|
||||
>
|
||||
Buy
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
private readonly _handleClick = async () => {
|
||||
// The button is disabled when there is no buy quote anyway.
|
||||
const { buyQuote, assetBuyer } = this.props;
|
||||
if (_.isUndefined(buyQuote) || _.isUndefined(assetBuyer)) {
|
||||
const { buyQuote, assetBuyer, affiliateInfo, accountAddress, accountEthBalanceInWei, web3Wrapper } = this.props;
|
||||
if (_.isUndefined(buyQuote) || _.isUndefined(accountAddress)) {
|
||||
return;
|
||||
}
|
||||
this.props.onValidationPending(buyQuote);
|
||||
const ethNeededForBuy = buyQuote.worstCaseQuoteInfo.totalEthAmount;
|
||||
// if we don't have a balance for the user, let the transaction through, it will be handled by the wallet
|
||||
const hasSufficientEth = _.isUndefined(accountEthBalanceInWei) || accountEthBalanceInWei.gte(ethNeededForBuy);
|
||||
if (!hasSufficientEth) {
|
||||
this.props.onValidationFail(buyQuote, ZeroExInstantError.InsufficientETH);
|
||||
return;
|
||||
}
|
||||
|
||||
let txHash: string | undefined;
|
||||
this.props.onAwaitingSignature(buyQuote);
|
||||
const gasInfo = await gasPriceEstimator.getGasInfoAsync();
|
||||
const feeRecipient = oc(affiliateInfo).feeRecipient();
|
||||
try {
|
||||
txHash = await assetBuyer.executeBuyQuoteAsync(buyQuote);
|
||||
txHash = await assetBuyer.executeBuyQuoteAsync(buyQuote, {
|
||||
feeRecipient,
|
||||
takerAddress: accountAddress,
|
||||
gasPrice: gasInfo.gasPriceInWei,
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof Error && e.message === AssetBuyerError.SignatureRequestDenied) {
|
||||
this.props.onSignatureDenied(buyQuote, e);
|
||||
return;
|
||||
if (e instanceof Error) {
|
||||
if (e.message === AssetBuyerError.SignatureRequestDenied) {
|
||||
this.props.onSignatureDenied(buyQuote);
|
||||
return;
|
||||
} else if (e.message === AssetBuyerError.TransactionValueTooLow) {
|
||||
this.props.onValidationFail(buyQuote, AssetBuyerError.TransactionValueTooLow);
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
this.props.onBuyProcessing(buyQuote, txHash);
|
||||
const startTimeUnix = new Date().getTime();
|
||||
const expectedEndTimeUnix = startTimeUnix + gasInfo.estimatedTimeMs;
|
||||
this.props.onBuyProcessing(buyQuote, txHash, startTimeUnix, expectedEndTimeUnix);
|
||||
try {
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(txHash);
|
||||
} catch (e) {
|
||||
|
||||
35
packages/instant/src/components/buy_order_progress.tsx
Normal file
35
packages/instant/src/components/buy_order_progress.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { TimedProgressBar } from '../components/timed_progress_bar';
|
||||
|
||||
import { TimeCounter } from '../components/time_counter';
|
||||
import { Container } from '../components/ui/container';
|
||||
import { OrderProcessState, OrderState } from '../types';
|
||||
|
||||
export interface BuyOrderProgressProps {
|
||||
buyOrderState: OrderState;
|
||||
}
|
||||
|
||||
export const BuyOrderProgress: React.StatelessComponent<BuyOrderProgressProps> = props => {
|
||||
const { buyOrderState } = props;
|
||||
|
||||
if (
|
||||
buyOrderState.processState === OrderProcessState.Processing ||
|
||||
buyOrderState.processState === OrderProcessState.Success ||
|
||||
buyOrderState.processState === OrderProcessState.Failure
|
||||
) {
|
||||
const progress = buyOrderState.progress;
|
||||
const hasEnded = buyOrderState.processState !== OrderProcessState.Processing;
|
||||
const expectedTimeMs = progress.expectedEndTimeUnix - progress.startTimeUnix;
|
||||
return (
|
||||
<Container padding="20px 20px 0px 20px" width="100%">
|
||||
<Container marginBottom="5px">
|
||||
<TimeCounter estimatedTimeMs={expectedTimeMs} hasEnded={hasEnded} key={progress.startTimeUnix} />
|
||||
</Container>
|
||||
<TimedProgressBar expectedTimeMs={expectedTimeMs} hasEnded={hasEnded} key={progress.startTimeUnix} />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
@@ -1,26 +0,0 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { PlacingOrderButton } from '../components/placing_order_button';
|
||||
import { SelectedAssetBuyButton } from '../containers/selected_asset_buy_button';
|
||||
import { SelectedAssetRetryButton } from '../containers/selected_asset_retry_button';
|
||||
import { SelectedAssetViewTransactionButton } from '../containers/selected_asset_view_transaction_button';
|
||||
import { OrderProcessState } from '../types';
|
||||
|
||||
export interface BuyOrderStateButtonProps {
|
||||
buyOrderProcessingState: OrderProcessState;
|
||||
}
|
||||
|
||||
export const BuyOrderStateButton: React.StatelessComponent<BuyOrderStateButtonProps> = props => {
|
||||
if (props.buyOrderProcessingState === OrderProcessState.FAILURE) {
|
||||
return <SelectedAssetRetryButton />;
|
||||
} else if (
|
||||
props.buyOrderProcessingState === OrderProcessState.SUCCESS ||
|
||||
props.buyOrderProcessingState === OrderProcessState.PROCESSING
|
||||
) {
|
||||
return <SelectedAssetViewTransactionButton />;
|
||||
} else if (props.buyOrderProcessingState === OrderProcessState.AWAITING_SIGNATURE) {
|
||||
return <PlacingOrderButton />;
|
||||
}
|
||||
|
||||
return <SelectedAssetBuyButton />;
|
||||
};
|
||||
71
packages/instant/src/components/buy_order_state_buttons.tsx
Normal file
71
packages/instant/src/components/buy_order_state_buttons.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
import { AssetBuyer, AssetBuyerError, BuyQuote } from '@0x/asset-buyer';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ColorOption } from '../style/theme';
|
||||
import { AffiliateInfo, OrderProcessState, ZeroExInstantError } from '../types';
|
||||
|
||||
import { BuyButton } from './buy_button';
|
||||
import { PlacingOrderButton } from './placing_order_button';
|
||||
import { SecondaryButton } from './secondary_button';
|
||||
|
||||
import { Button } from './ui/button';
|
||||
import { Flex } from './ui/flex';
|
||||
|
||||
export interface BuyOrderStateButtonProps {
|
||||
accountAddress?: string;
|
||||
accountEthBalanceInWei?: BigNumber;
|
||||
buyQuote?: BuyQuote;
|
||||
buyOrderProcessingState: OrderProcessState;
|
||||
assetBuyer: AssetBuyer;
|
||||
web3Wrapper: Web3Wrapper;
|
||||
affiliateInfo?: AffiliateInfo;
|
||||
onViewTransaction: () => void;
|
||||
onValidationPending: (buyQuote: BuyQuote) => void;
|
||||
onValidationFail: (buyQuote: BuyQuote, errorMessage: AssetBuyerError | ZeroExInstantError) => void;
|
||||
onSignatureDenied: (buyQuote: BuyQuote) => void;
|
||||
onBuyProcessing: (buyQuote: BuyQuote, txHash: string, startTimeUnix: number, expectedEndTimeUnix: number) => void;
|
||||
onBuySuccess: (buyQuote: BuyQuote, txHash: string) => void;
|
||||
onBuyFailure: (buyQuote: BuyQuote, txHash: string) => void;
|
||||
onRetry: () => void;
|
||||
}
|
||||
|
||||
export const BuyOrderStateButtons: React.StatelessComponent<BuyOrderStateButtonProps> = props => {
|
||||
if (props.buyOrderProcessingState === OrderProcessState.Failure) {
|
||||
return (
|
||||
<Flex justify="space-between">
|
||||
<Button width="48%" onClick={props.onRetry} fontColor={ColorOption.white} fontSize="16px">
|
||||
Back
|
||||
</Button>
|
||||
<SecondaryButton width="48%" onClick={props.onViewTransaction}>
|
||||
Details
|
||||
</SecondaryButton>
|
||||
</Flex>
|
||||
);
|
||||
} else if (
|
||||
props.buyOrderProcessingState === OrderProcessState.Success ||
|
||||
props.buyOrderProcessingState === OrderProcessState.Processing
|
||||
) {
|
||||
return <SecondaryButton onClick={props.onViewTransaction}>View Transaction</SecondaryButton>;
|
||||
} else if (props.buyOrderProcessingState === OrderProcessState.Validating) {
|
||||
return <PlacingOrderButton />;
|
||||
}
|
||||
|
||||
return (
|
||||
<BuyButton
|
||||
accountAddress={props.accountAddress}
|
||||
accountEthBalanceInWei={props.accountEthBalanceInWei}
|
||||
buyQuote={props.buyQuote}
|
||||
assetBuyer={props.assetBuyer}
|
||||
web3Wrapper={props.web3Wrapper}
|
||||
affiliateInfo={props.affiliateInfo}
|
||||
onValidationPending={props.onValidationPending}
|
||||
onValidationFail={props.onValidationFail}
|
||||
onSignatureDenied={props.onSignatureDenied}
|
||||
onBuyProcessing={props.onBuyProcessing}
|
||||
onBuySuccess={props.onBuySuccess}
|
||||
onBuyFailure={props.onBuyFailure}
|
||||
/>
|
||||
);
|
||||
};
|
||||
32
packages/instant/src/components/css_reset.tsx
Normal file
32
packages/instant/src/components/css_reset.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { INJECTED_DIV_CLASS } from '../constants';
|
||||
import { createGlobalStyle } from '../style/theme';
|
||||
|
||||
export interface CSSResetProps {}
|
||||
|
||||
/*
|
||||
* Derived from
|
||||
* https://github.com/jtrost/Complete-CSS-Reset
|
||||
*/
|
||||
export const CSSReset = createGlobalStyle`
|
||||
.${INJECTED_DIV_CLASS} {
|
||||
a, abbr, area, article, aside, audio, b, bdo, blockquote, body, button,
|
||||
canvas, caption, cite, code, col, colgroup, command, datalist, dd, del,
|
||||
details, dialog, dfn, div, dl, dt, em, embed, fieldset, figure, form,
|
||||
h1, h2, h3, h4, h5, h6, head, header, hgroup, hr, html, i, iframe, img,
|
||||
input, ins, keygen, kbd, label, legend, li, map, mark, menu, meter, nav,
|
||||
noscript, object, ol, optgroup, option, output, p, param, pre, progress,
|
||||
q, rp, rt, ruby, samp, section, select, small, span, strong, sub, sup,
|
||||
table, tbody, td, textarea, tfoot, th, thead, time, tr, ul, var, video {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
font-size: 100%;
|
||||
font: inherit;
|
||||
margin: 0;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
text-align: left;
|
||||
text-decoration: none;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
}
|
||||
`;
|
||||
161
packages/instant/src/components/erc20_asset_amount_input.tsx
Normal file
161
packages/instant/src/components/erc20_asset_amount_input.tsx
Normal file
@@ -0,0 +1,161 @@
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ColorOption, transparentWhite } from '../style/theme';
|
||||
import { ERC20Asset, SimpleHandler } from '../types';
|
||||
import { assetUtils } from '../util/asset';
|
||||
import { util } from '../util/util';
|
||||
|
||||
import { ScalingAmountInput } from './scaling_amount_input';
|
||||
|
||||
import { Container } from './ui/container';
|
||||
import { Flex } from './ui/flex';
|
||||
import { Icon } from './ui/icon';
|
||||
import { Text } from './ui/text';
|
||||
|
||||
// Asset amounts only apply to ERC20 assets
|
||||
export interface ERC20AssetAmountInputProps {
|
||||
asset?: ERC20Asset;
|
||||
value?: BigNumber;
|
||||
onChange: (value?: BigNumber, asset?: ERC20Asset) => void;
|
||||
onSelectAssetClick?: (asset?: ERC20Asset) => void;
|
||||
startingFontSizePx: number;
|
||||
fontColor?: ColorOption;
|
||||
isDisabled: boolean;
|
||||
numberOfAssetsAvailable?: number;
|
||||
}
|
||||
|
||||
export interface ERC20AssetAmountInputState {
|
||||
currentFontSizePx: number;
|
||||
}
|
||||
|
||||
export class ERC20AssetAmountInput extends React.Component<ERC20AssetAmountInputProps, ERC20AssetAmountInputState> {
|
||||
public static defaultProps = {
|
||||
onChange: util.boundNoop,
|
||||
isDisabled: false,
|
||||
};
|
||||
constructor(props: ERC20AssetAmountInputProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
currentFontSizePx: props.startingFontSizePx,
|
||||
};
|
||||
}
|
||||
public render(): React.ReactNode {
|
||||
const { asset } = this.props;
|
||||
return (
|
||||
<Container whiteSpace="nowrap">
|
||||
{_.isUndefined(asset) ? this._renderTokenSelectionContent() : this._renderContentForAsset(asset)}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
private readonly _renderContentForAsset = (asset: ERC20Asset): React.ReactNode => {
|
||||
const { onChange, ...rest } = this.props;
|
||||
const amountBorderBottom = this.props.isDisabled ? '' : `1px solid ${transparentWhite}`;
|
||||
const onSymbolClick = this._generateSelectAssetClickHandler();
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Container borderBottom={amountBorderBottom} display="inline-block">
|
||||
<ScalingAmountInput
|
||||
{...rest}
|
||||
textLengthThreshold={this._textLengthThresholdForAsset(asset)}
|
||||
maxFontSizePx={this.props.startingFontSizePx}
|
||||
onAmountChange={this._handleChange}
|
||||
onFontSizeChange={this._handleFontSizeChange}
|
||||
/>
|
||||
</Container>
|
||||
<Container
|
||||
display="inline-block"
|
||||
marginLeft="8px"
|
||||
title={assetUtils.bestNameForAsset(asset, undefined)}
|
||||
>
|
||||
<Flex inline={true}>
|
||||
<Text
|
||||
fontSize={`${this.state.currentFontSizePx}px`}
|
||||
fontColor={ColorOption.white}
|
||||
textTransform="uppercase"
|
||||
onClick={onSymbolClick}
|
||||
>
|
||||
{assetUtils.formattedSymbolForAsset(asset)}
|
||||
</Text>
|
||||
{this._renderChevronIcon()}
|
||||
</Flex>
|
||||
</Container>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
private readonly _renderTokenSelectionContent = (): React.ReactNode => {
|
||||
const { numberOfAssetsAvailable } = this.props;
|
||||
let text = 'Select Token';
|
||||
if (_.isUndefined(numberOfAssetsAvailable)) {
|
||||
text = 'Loading...';
|
||||
} else if (numberOfAssetsAvailable === 0) {
|
||||
text = 'Assets Unavailable';
|
||||
}
|
||||
return (
|
||||
<Flex>
|
||||
<Text
|
||||
fontSize="30px"
|
||||
fontColor={ColorOption.white}
|
||||
opacity={0.7}
|
||||
fontWeight="500"
|
||||
onClick={this._generateSelectAssetClickHandler()}
|
||||
>
|
||||
{text}
|
||||
</Text>
|
||||
{this._renderChevronIcon()}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
private readonly _renderChevronIcon = (): React.ReactNode => {
|
||||
if (!this._areMultipleAssetsAvailable()) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Container marginLeft="5px">
|
||||
<Icon icon="chevron" width={12} stroke={ColorOption.white} onClick={this._handleSelectAssetClick} />
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
private readonly _handleChange = (value?: BigNumber): void => {
|
||||
this.props.onChange(value, this.props.asset);
|
||||
};
|
||||
private readonly _handleFontSizeChange = (fontSizePx: number): void => {
|
||||
this.setState({
|
||||
currentFontSizePx: fontSizePx,
|
||||
});
|
||||
};
|
||||
private readonly _generateSelectAssetClickHandler = (): SimpleHandler | undefined => {
|
||||
// We don't want to allow opening the token selection panel if there are no assets.
|
||||
// Since styles are inferred from the presence of a click handler, we want to return undefined
|
||||
// instead of providing a noop.
|
||||
if (!this._areMultipleAssetsAvailable() || _.isUndefined(this.props.onSelectAssetClick)) {
|
||||
return undefined;
|
||||
}
|
||||
return this._handleSelectAssetClick;
|
||||
};
|
||||
private readonly _areMultipleAssetsAvailable = (): boolean => {
|
||||
const { numberOfAssetsAvailable } = this.props;
|
||||
return !_.isUndefined(numberOfAssetsAvailable) && numberOfAssetsAvailable > 1;
|
||||
};
|
||||
private readonly _handleSelectAssetClick = (): void => {
|
||||
if (this.props.onSelectAssetClick) {
|
||||
this.props.onSelectAssetClick();
|
||||
}
|
||||
};
|
||||
// For assets with symbols of different length,
|
||||
// start scaling the input at different character lengths
|
||||
private readonly _textLengthThresholdForAsset = (asset?: ERC20Asset): number => {
|
||||
if (_.isUndefined(asset)) {
|
||||
return 3;
|
||||
}
|
||||
const symbol = asset.metaData.symbol;
|
||||
if (symbol.length <= 3) {
|
||||
return 5;
|
||||
}
|
||||
if (symbol.length === 5) {
|
||||
return 3;
|
||||
}
|
||||
return 4;
|
||||
};
|
||||
}
|
||||
111
packages/instant/src/components/erc20_token_selector.tsx
Normal file
111
packages/instant/src/components/erc20_token_selector.tsx
Normal file
@@ -0,0 +1,111 @@
|
||||
import * as _ from 'lodash';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ColorOption } from '../style/theme';
|
||||
import { ERC20Asset } from '../types';
|
||||
import { assetUtils } from '../util/asset';
|
||||
|
||||
import { SearchInput } from './search_input';
|
||||
|
||||
import { Circle } from './ui/circle';
|
||||
import { Container } from './ui/container';
|
||||
import { Flex } from './ui/flex';
|
||||
import { Text } from './ui/text';
|
||||
|
||||
export interface ERC20TokenSelectorProps {
|
||||
tokens: ERC20Asset[];
|
||||
onTokenSelect: (token: ERC20Asset) => void;
|
||||
}
|
||||
|
||||
export interface ERC20TokenSelectorState {
|
||||
searchQuery?: string;
|
||||
}
|
||||
|
||||
export class ERC20TokenSelector extends React.Component<ERC20TokenSelectorProps> {
|
||||
public state: ERC20TokenSelectorState = {
|
||||
searchQuery: undefined,
|
||||
};
|
||||
public render(): React.ReactNode {
|
||||
const { tokens, onTokenSelect } = this.props;
|
||||
return (
|
||||
<Container height="100%">
|
||||
<SearchInput
|
||||
placeholder="Search tokens..."
|
||||
width="100%"
|
||||
value={this.state.searchQuery}
|
||||
onChange={this._handleSearchInputChange}
|
||||
/>
|
||||
<Container overflow="scroll" height="calc(100% - 80px)" marginTop="10px">
|
||||
{_.map(tokens, token => {
|
||||
if (!this._isTokenQueryMatch(token)) {
|
||||
return null;
|
||||
}
|
||||
return <TokenSelectorRow key={token.assetData} token={token} onClick={onTokenSelect} />;
|
||||
})}
|
||||
</Container>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
private readonly _handleSearchInputChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
const searchQuery = event.target.value;
|
||||
this.setState({
|
||||
searchQuery,
|
||||
});
|
||||
};
|
||||
private readonly _isTokenQueryMatch = (token: ERC20Asset): boolean => {
|
||||
const { searchQuery } = this.state;
|
||||
if (_.isUndefined(searchQuery)) {
|
||||
return true;
|
||||
}
|
||||
const stringToSearch = `${token.metaData.name} ${token.metaData.symbol}`;
|
||||
return _.includes(stringToSearch.toLowerCase(), searchQuery.toLowerCase());
|
||||
};
|
||||
}
|
||||
|
||||
interface TokenSelectorRowProps {
|
||||
token: ERC20Asset;
|
||||
onClick: (token: ERC20Asset) => void;
|
||||
}
|
||||
|
||||
class TokenSelectorRow extends React.Component<TokenSelectorRowProps> {
|
||||
public render(): React.ReactNode {
|
||||
const { token } = this.props;
|
||||
const displaySymbol = assetUtils.bestNameForAsset(token);
|
||||
return (
|
||||
<Container
|
||||
padding="12px 0px"
|
||||
borderBottom="1px solid"
|
||||
borderColor={ColorOption.feintGrey}
|
||||
backgroundColor={ColorOption.white}
|
||||
width="100%"
|
||||
onClick={this._handleClick}
|
||||
darkenOnHover={true}
|
||||
cursor="pointer"
|
||||
>
|
||||
<Container marginLeft="5px">
|
||||
<Flex justify="flex-start">
|
||||
<Container marginRight="10px">
|
||||
<Circle diameter={30} rawColor={token.metaData.primaryColor}>
|
||||
<Flex height="100%">
|
||||
<Text fontColor={ColorOption.white} fontSize="8px">
|
||||
{displaySymbol}
|
||||
</Text>
|
||||
</Flex>
|
||||
</Circle>
|
||||
</Container>
|
||||
<Text fontSize="14px" fontWeight={700} fontColor={ColorOption.black}>
|
||||
{displaySymbol}
|
||||
</Text>
|
||||
<Container margin="0px 5px">
|
||||
<Text fontSize="14px"> - </Text>
|
||||
</Container>
|
||||
<Text fontSize="14px">{token.metaData.name}</Text>
|
||||
</Flex>
|
||||
</Container>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
private readonly _handleClick = (): void => {
|
||||
this.props.onClick(this.props.token);
|
||||
};
|
||||
}
|
||||
@@ -2,15 +2,17 @@ import { BigNumber } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
import * as React from 'react';
|
||||
|
||||
import { SelectedAssetAmountInput } from '../containers/selected_asset_amount_input';
|
||||
import { SelectedERC20AssetAmountInput } from '../containers/selected_erc20_asset_amount_input';
|
||||
import { ColorOption } from '../style/theme';
|
||||
import { AsyncProcessState, OrderProcessState, OrderState } from '../types';
|
||||
import { AsyncProcessState, ERC20Asset, OrderProcessState, OrderState } from '../types';
|
||||
import { format } from '../util/format';
|
||||
|
||||
import { AmountPlaceholder } from './amount_placeholder';
|
||||
import { Container, Flex, Text } from './ui';
|
||||
import { Container } from './ui/container';
|
||||
import { Flex } from './ui/flex';
|
||||
import { Icon } from './ui/icon';
|
||||
import { Spinner } from './ui/spinner';
|
||||
import { Text } from './ui/text';
|
||||
|
||||
export interface InstantHeadingProps {
|
||||
selectedAssetAmount?: BigNumber;
|
||||
@@ -18,6 +20,7 @@ export interface InstantHeadingProps {
|
||||
ethUsdPrice?: BigNumber;
|
||||
quoteRequestState: AsyncProcessState;
|
||||
buyOrderState: OrderState;
|
||||
onSelectAssetClick?: (asset?: ERC20Asset) => void;
|
||||
}
|
||||
|
||||
const PLACEHOLDER_COLOR = ColorOption.white;
|
||||
@@ -48,7 +51,12 @@ export class InstantHeading extends React.Component<InstantHeadingProps, {}> {
|
||||
</Text>
|
||||
</Container>
|
||||
<Flex direction="row" justify="space-between">
|
||||
<SelectedAssetAmountInput fontSize="45px" />
|
||||
<Flex height="60px">
|
||||
<SelectedERC20AssetAmountInput
|
||||
startingFontSizePx={38}
|
||||
onSelectAssetClick={this.props.onSelectAssetClick}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex direction="column" justify="space-between">
|
||||
{iconOrAmounts}
|
||||
</Flex>
|
||||
@@ -60,8 +68,8 @@ export class InstantHeading extends React.Component<InstantHeadingProps, {}> {
|
||||
private _renderAmountsSection(): React.ReactNode {
|
||||
return (
|
||||
<Container>
|
||||
<Container marginBottom="5px">{this._placeholderOrAmount(this._ethAmount)}</Container>
|
||||
<Container opacity={0.7}>{this._placeholderOrAmount(this._dollarAmount)}</Container>
|
||||
<Container marginBottom="5px">{this._renderPlaceholderOrAmount(this._renderEthAmount)}</Container>
|
||||
<Container opacity={0.7}>{this._renderPlaceholderOrAmount(this._renderDollarAmount)}</Container>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -69,31 +77,31 @@ export class InstantHeading extends React.Component<InstantHeadingProps, {}> {
|
||||
private _renderIcon(): React.ReactNode {
|
||||
const processState = this.props.buyOrderState.processState;
|
||||
|
||||
if (processState === OrderProcessState.FAILURE) {
|
||||
return <Icon icon={'failed'} width={ICON_WIDTH} height={ICON_HEIGHT} color={ICON_COLOR} />;
|
||||
} else if (processState === OrderProcessState.PROCESSING) {
|
||||
if (processState === OrderProcessState.Failure) {
|
||||
return <Icon icon="failed" width={ICON_WIDTH} height={ICON_HEIGHT} color={ICON_COLOR} />;
|
||||
} else if (processState === OrderProcessState.Processing) {
|
||||
return <Spinner widthPx={ICON_HEIGHT} heightPx={ICON_HEIGHT} />;
|
||||
} else if (processState === OrderProcessState.SUCCESS) {
|
||||
return <Icon icon={'success'} width={ICON_WIDTH} height={ICON_HEIGHT} color={ICON_COLOR} />;
|
||||
} else if (processState === OrderProcessState.Success) {
|
||||
return <Icon icon="success" width={ICON_WIDTH} height={ICON_HEIGHT} color={ICON_COLOR} />;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private _renderTopText(): React.ReactNode {
|
||||
const processState = this.props.buyOrderState.processState;
|
||||
if (processState === OrderProcessState.FAILURE) {
|
||||
if (processState === OrderProcessState.Failure) {
|
||||
return 'Order failed';
|
||||
} else if (processState === OrderProcessState.PROCESSING) {
|
||||
} else if (processState === OrderProcessState.Processing) {
|
||||
return 'Processing Order...';
|
||||
} else if (processState === OrderProcessState.SUCCESS) {
|
||||
} else if (processState === OrderProcessState.Success) {
|
||||
return 'Tokens received!';
|
||||
}
|
||||
|
||||
return 'I want to buy';
|
||||
}
|
||||
|
||||
private _placeholderOrAmount(amountFunction: () => React.ReactNode): React.ReactNode {
|
||||
if (this.props.quoteRequestState === AsyncProcessState.PENDING) {
|
||||
private _renderPlaceholderOrAmount(amountFunction: () => React.ReactNode): React.ReactNode {
|
||||
if (this.props.quoteRequestState === AsyncProcessState.Pending) {
|
||||
return <AmountPlaceholder isPulsating={true} color={PLACEHOLDER_COLOR} />;
|
||||
}
|
||||
if (_.isUndefined(this.props.selectedAssetAmount)) {
|
||||
@@ -102,7 +110,7 @@ export class InstantHeading extends React.Component<InstantHeadingProps, {}> {
|
||||
return amountFunction();
|
||||
}
|
||||
|
||||
private readonly _ethAmount = (): React.ReactNode => {
|
||||
private readonly _renderEthAmount = (): React.ReactNode => {
|
||||
return (
|
||||
<Text fontSize="16px" fontColor={ColorOption.white} fontWeight={500}>
|
||||
{format.ethBaseAmount(
|
||||
@@ -114,7 +122,7 @@ export class InstantHeading extends React.Component<InstantHeadingProps, {}> {
|
||||
);
|
||||
};
|
||||
|
||||
private readonly _dollarAmount = (): React.ReactNode => {
|
||||
private readonly _renderDollarAmount = (): React.ReactNode => {
|
||||
return (
|
||||
<Text fontSize="16px" fontColor={ColorOption.white}>
|
||||
{format.ethBaseAmountInUsd(
|
||||
|
||||
@@ -8,7 +8,10 @@ import { ColorOption } from '../style/theme';
|
||||
import { format } from '../util/format';
|
||||
|
||||
import { AmountPlaceholder } from './amount_placeholder';
|
||||
import { Container, Flex, Text } from './ui';
|
||||
|
||||
import { Container } from './ui/container';
|
||||
import { Flex } from './ui/flex';
|
||||
import { Text } from './ui/text';
|
||||
|
||||
export interface OrderDetailsProps {
|
||||
buyQuoteInfo?: BuyQuoteInfo;
|
||||
@@ -23,7 +26,7 @@ export class OrderDetails extends React.Component<OrderDetailsProps> {
|
||||
const ethTokenFee = buyQuoteAccessor.feeEthAmount();
|
||||
const totalEthAmount = buyQuoteAccessor.totalEthAmount();
|
||||
return (
|
||||
<Container padding="20px" width="100%">
|
||||
<Container padding="20px" width="100%" flexGrow={1}>
|
||||
<Container marginBottom="10px">
|
||||
<Text
|
||||
letterSpacing="1px"
|
||||
|
||||
45
packages/instant/src/components/payment_method.tsx
Normal file
45
packages/instant/src/components/payment_method.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ColorOption } from '../style/theme';
|
||||
import { Network } from '../types';
|
||||
|
||||
import { PaymentMethodDropdown } from './payment_method_dropdown';
|
||||
import { Circle } from './ui/circle';
|
||||
import { Container } from './ui/container';
|
||||
import { Flex } from './ui/flex';
|
||||
import { Text } from './ui/text';
|
||||
|
||||
export interface PaymentMethodProps {}
|
||||
|
||||
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>
|
||||
<Flex>
|
||||
<Circle color={ColorOption.green} diameter={8} />
|
||||
<Container marginLeft="3px">
|
||||
<Text fontColor={ColorOption.darkGrey} fontSize="12px">
|
||||
MetaMask
|
||||
</Text>
|
||||
</Container>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Container>
|
||||
<PaymentMethodDropdown
|
||||
accountAddress="0xa1b2c3d4e5f6g7h8j9k10"
|
||||
accountEthBalanceInWei={new BigNumber(10500000000000000000)}
|
||||
network={Network.Mainnet}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
44
packages/instant/src/components/payment_method_dropdown.tsx
Normal file
44
packages/instant/src/components/payment_method_dropdown.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import copy from 'copy-to-clipboard';
|
||||
import * as React from 'react';
|
||||
|
||||
import { Network } from '../types';
|
||||
import { etherscanUtil } from '../util/etherscan';
|
||||
import { format } from '../util/format';
|
||||
|
||||
import { Dropdown, DropdownItemConfig } from './ui/dropdown';
|
||||
|
||||
export interface PaymentMethodDropdownProps {
|
||||
accountAddress: string;
|
||||
accountEthBalanceInWei?: BigNumber;
|
||||
network: Network;
|
||||
}
|
||||
|
||||
export class PaymentMethodDropdown extends React.Component<PaymentMethodDropdownProps> {
|
||||
public render(): React.ReactNode {
|
||||
const { accountAddress, accountEthBalanceInWei } = this.props;
|
||||
const value = format.ethAddress(accountAddress);
|
||||
const label = format.ethBaseAmount(accountEthBalanceInWei, 4, '') as string;
|
||||
return <Dropdown value={value} label={label} items={this._getDropdownItemConfigs()} />;
|
||||
}
|
||||
private readonly _getDropdownItemConfigs = (): DropdownItemConfig[] => {
|
||||
const viewOnEtherscan = {
|
||||
text: 'View on Etherscan',
|
||||
onClick: this._handleEtherscanClick,
|
||||
};
|
||||
const copyAddressToClipboard = {
|
||||
text: 'Copy address to clipboard',
|
||||
onClick: this._handleCopyToClipboardClick,
|
||||
};
|
||||
return [viewOnEtherscan, copyAddressToClipboard];
|
||||
};
|
||||
private readonly _handleEtherscanClick = (): void => {
|
||||
const { accountAddress, network } = this.props;
|
||||
const etherscanUrl = etherscanUtil.getEtherScanEthAddressIfExists(accountAddress, network);
|
||||
window.open(etherscanUrl, '_blank');
|
||||
};
|
||||
private readonly _handleCopyToClipboardClick = (): void => {
|
||||
const { accountAddress } = this.props;
|
||||
copy(accountAddress);
|
||||
};
|
||||
}
|
||||
@@ -5,15 +5,12 @@ import { ColorOption } from '../style/theme';
|
||||
import { Button } from './ui/button';
|
||||
import { Container } from './ui/container';
|
||||
import { Spinner } from './ui/spinner';
|
||||
import { Text } from './ui/text';
|
||||
|
||||
export const PlacingOrderButton: React.StatelessComponent<{}> = props => (
|
||||
<Button isDisabled={true} width="100%">
|
||||
<Button isDisabled={true} width="100%" fontColor={ColorOption.white} fontSize="20px">
|
||||
<Container display="inline-block" position="relative" top="3px" marginRight="8px">
|
||||
<Spinner widthPx={20} heightPx={20} />
|
||||
</Container>
|
||||
<Text fontColor={ColorOption.white} fontWeight={600} fontSize="20px">
|
||||
Placing Order…
|
||||
</Text>
|
||||
Placing Order…
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { SecondaryButton } from './secondary_button';
|
||||
|
||||
export interface RetryButtonProps {
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
export const RetryButton: React.StatelessComponent<RetryButtonProps> = props => {
|
||||
return <SecondaryButton onClick={props.onClick}>Try Again</SecondaryButton>;
|
||||
};
|
||||
83
packages/instant/src/components/scaling_amount_input.tsx
Normal file
83
packages/instant/src/components/scaling_amount_input.tsx
Normal file
@@ -0,0 +1,83 @@
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
import * as React from 'react';
|
||||
|
||||
import { Maybe } from '../types';
|
||||
|
||||
import { ColorOption } from '../style/theme';
|
||||
import { maybeBigNumberUtil } from '../util/maybe_big_number';
|
||||
import { util } from '../util/util';
|
||||
|
||||
import { ScalingInput } from './scaling_input';
|
||||
|
||||
export interface ScalingAmountInputProps {
|
||||
isDisabled: boolean;
|
||||
maxFontSizePx: number;
|
||||
textLengthThreshold: number;
|
||||
fontColor?: ColorOption;
|
||||
value?: BigNumber;
|
||||
onAmountChange: (value?: BigNumber) => void;
|
||||
onFontSizeChange: (fontSizePx: number) => void;
|
||||
}
|
||||
interface ScalingAmountInputState {
|
||||
stringValue: string;
|
||||
}
|
||||
|
||||
const { stringToMaybeBigNumber, areMaybeBigNumbersEqual } = maybeBigNumberUtil;
|
||||
export class ScalingAmountInput extends React.Component<ScalingAmountInputProps, ScalingAmountInputState> {
|
||||
public static defaultProps = {
|
||||
onAmountChange: util.boundNoop,
|
||||
onFontSizeChange: util.boundNoop,
|
||||
isDisabled: false,
|
||||
};
|
||||
public constructor(props: ScalingAmountInputProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
stringValue: _.isUndefined(props.value) ? '' : props.value.toString(),
|
||||
};
|
||||
}
|
||||
public componentDidUpdate(): void {
|
||||
const parsedStateValue = stringToMaybeBigNumber(this.state.stringValue);
|
||||
const currentValue = this.props.value;
|
||||
|
||||
if (!areMaybeBigNumbersEqual(parsedStateValue, currentValue)) {
|
||||
// we somehow got into the state in which the value passed in and the string value
|
||||
// in state have differed, reset state
|
||||
// we dont expect to ever get into this state, but let's make sure
|
||||
// we reset if we do since we're dealing with important numbers
|
||||
this.setState({
|
||||
stringValue: _.isUndefined(currentValue) ? '' : currentValue.toString(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public render(): React.ReactNode {
|
||||
const { textLengthThreshold, fontColor, maxFontSizePx, onFontSizeChange } = this.props;
|
||||
return (
|
||||
<ScalingInput
|
||||
maxFontSizePx={maxFontSizePx}
|
||||
textLengthThreshold={textLengthThreshold}
|
||||
onFontSizeChange={onFontSizeChange}
|
||||
fontColor={fontColor}
|
||||
onChange={this._handleChange}
|
||||
value={this.state.stringValue}
|
||||
placeholder="0.00"
|
||||
emptyInputWidthCh={3.5}
|
||||
isDisabled={this.props.isDisabled}
|
||||
/>
|
||||
);
|
||||
}
|
||||
private readonly _handleChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
const sanitizedValue = event.target.value.replace(/[^0-9.]/g, ''); // only allow numbers and "."
|
||||
this.setState({
|
||||
stringValue: sanitizedValue,
|
||||
});
|
||||
|
||||
// Trigger onAmountChange with a valid BigNumber, or undefined if the sanitizedValue is invalid or empty
|
||||
const bigNumberValue: Maybe<BigNumber> = _.isEmpty(sanitizedValue)
|
||||
? undefined
|
||||
: stringToMaybeBigNumber(sanitizedValue);
|
||||
|
||||
this.props.onAmountChange(bigNumberValue);
|
||||
};
|
||||
}
|
||||
173
packages/instant/src/components/scaling_input.tsx
Normal file
173
packages/instant/src/components/scaling_input.tsx
Normal file
@@ -0,0 +1,173 @@
|
||||
import * as _ from 'lodash';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ColorOption } from '../style/theme';
|
||||
import { util } from '../util/util';
|
||||
|
||||
import { Input } from './ui/input';
|
||||
|
||||
export enum ScalingInputPhase {
|
||||
FixedFontSize,
|
||||
ScalingFontSize,
|
||||
}
|
||||
|
||||
export interface ScalingSettings {
|
||||
percentageToReduceFontSizePerCharacter: number;
|
||||
constantPxToIncreaseWidthPerCharacter: number;
|
||||
}
|
||||
|
||||
export interface ScalingInputProps {
|
||||
textLengthThreshold: number;
|
||||
maxFontSizePx: number;
|
||||
value: string;
|
||||
emptyInputWidthCh: number;
|
||||
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
onFontSizeChange: (fontSizePx: number) => void;
|
||||
fontColor?: ColorOption;
|
||||
placeholder?: string;
|
||||
maxLength?: number;
|
||||
scalingSettings: ScalingSettings;
|
||||
isDisabled: boolean;
|
||||
}
|
||||
|
||||
export interface ScalingInputState {
|
||||
inputWidthPxAtPhaseChange?: number;
|
||||
}
|
||||
|
||||
export interface ScalingInputSnapshot {
|
||||
inputWidthPx: number;
|
||||
}
|
||||
|
||||
// These are magic numbers that were determined experimentally.
|
||||
const defaultScalingSettings: ScalingSettings = {
|
||||
percentageToReduceFontSizePerCharacter: 0.125,
|
||||
constantPxToIncreaseWidthPerCharacter: 4,
|
||||
};
|
||||
|
||||
export class ScalingInput extends React.Component<ScalingInputProps, ScalingInputState> {
|
||||
public static defaultProps = {
|
||||
onChange: util.boundNoop,
|
||||
onFontSizeChange: util.boundNoop,
|
||||
maxLength: 7,
|
||||
scalingSettings: defaultScalingSettings,
|
||||
isDisabled: false,
|
||||
};
|
||||
public state: ScalingInputState = {
|
||||
inputWidthPxAtPhaseChange: undefined,
|
||||
};
|
||||
private readonly _inputRef = React.createRef<HTMLInputElement>();
|
||||
public static getPhase(textLengthThreshold: number, value: string): ScalingInputPhase {
|
||||
if (value.length <= textLengthThreshold) {
|
||||
return ScalingInputPhase.FixedFontSize;
|
||||
}
|
||||
return ScalingInputPhase.ScalingFontSize;
|
||||
}
|
||||
public static getPhaseFromProps(props: ScalingInputProps): ScalingInputPhase {
|
||||
const { value, textLengthThreshold } = props;
|
||||
return ScalingInput.getPhase(textLengthThreshold, value);
|
||||
}
|
||||
public static calculateFontSize(
|
||||
textLengthThreshold: number,
|
||||
maxFontSizePx: number,
|
||||
phase: ScalingInputPhase,
|
||||
value: string,
|
||||
percentageToReduceFontSizePerCharacter: number,
|
||||
): number {
|
||||
if (phase !== ScalingInputPhase.ScalingFontSize) {
|
||||
return maxFontSizePx;
|
||||
}
|
||||
const charactersOverMax = value.length - textLengthThreshold;
|
||||
const scalingFactor = (1 - percentageToReduceFontSizePerCharacter) ** charactersOverMax;
|
||||
const fontSize = scalingFactor * maxFontSizePx;
|
||||
return fontSize;
|
||||
}
|
||||
public static calculateFontSizeFromProps(props: ScalingInputProps, phase: ScalingInputPhase): number {
|
||||
const { textLengthThreshold, value, maxFontSizePx, scalingSettings } = props;
|
||||
return ScalingInput.calculateFontSize(
|
||||
textLengthThreshold,
|
||||
maxFontSizePx,
|
||||
phase,
|
||||
value,
|
||||
scalingSettings.percentageToReduceFontSizePerCharacter,
|
||||
);
|
||||
}
|
||||
public getSnapshotBeforeUpdate(): ScalingInputSnapshot {
|
||||
return {
|
||||
inputWidthPx: this._getInputWidthInPx(),
|
||||
};
|
||||
}
|
||||
public componentDidUpdate(
|
||||
prevProps: ScalingInputProps,
|
||||
prevState: ScalingInputState,
|
||||
snapshot: ScalingInputSnapshot,
|
||||
): void {
|
||||
const prevPhase = ScalingInput.getPhaseFromProps(prevProps);
|
||||
const curPhase = ScalingInput.getPhaseFromProps(this.props);
|
||||
// if we went from fixed to scaling, save the width from the transition
|
||||
if (prevPhase !== ScalingInputPhase.ScalingFontSize && curPhase === ScalingInputPhase.ScalingFontSize) {
|
||||
this.setState({
|
||||
inputWidthPxAtPhaseChange: snapshot.inputWidthPx,
|
||||
});
|
||||
}
|
||||
// if we went from scaling to fixed, revert back to scaling using `ch`
|
||||
if (prevPhase === ScalingInputPhase.ScalingFontSize && curPhase !== ScalingInputPhase.ScalingFontSize) {
|
||||
this.setState({
|
||||
inputWidthPxAtPhaseChange: undefined,
|
||||
});
|
||||
}
|
||||
const prevFontSize = ScalingInput.calculateFontSizeFromProps(prevProps, prevPhase);
|
||||
const curFontSize = ScalingInput.calculateFontSizeFromProps(this.props, curPhase);
|
||||
// If font size has changed, notify.
|
||||
if (prevFontSize !== curFontSize) {
|
||||
this.props.onFontSizeChange(curFontSize);
|
||||
}
|
||||
}
|
||||
public render(): React.ReactNode {
|
||||
const { isDisabled, fontColor, onChange, placeholder, value, maxLength } = this.props;
|
||||
const phase = ScalingInput.getPhaseFromProps(this.props);
|
||||
return (
|
||||
<Input
|
||||
ref={this._inputRef as any}
|
||||
fontColor={fontColor}
|
||||
onChange={onChange}
|
||||
value={value}
|
||||
placeholder={placeholder}
|
||||
fontSize={`${this._calculateFontSize(phase)}px`}
|
||||
width={this._calculateWidth(phase)}
|
||||
maxLength={maxLength}
|
||||
disabled={isDisabled}
|
||||
/>
|
||||
);
|
||||
}
|
||||
private readonly _calculateWidth = (phase: ScalingInputPhase): string => {
|
||||
const { value, textLengthThreshold, scalingSettings } = this.props;
|
||||
if (_.isEmpty(value)) {
|
||||
return `${this.props.emptyInputWidthCh}ch`;
|
||||
}
|
||||
switch (phase) {
|
||||
case ScalingInputPhase.FixedFontSize:
|
||||
return `${value.length}ch`;
|
||||
case ScalingInputPhase.ScalingFontSize:
|
||||
const { inputWidthPxAtPhaseChange } = this.state;
|
||||
if (!_.isUndefined(inputWidthPxAtPhaseChange)) {
|
||||
const charactersOverMax = value.length - textLengthThreshold;
|
||||
const scalingAmount = scalingSettings.constantPxToIncreaseWidthPerCharacter * charactersOverMax;
|
||||
const width = inputWidthPxAtPhaseChange + scalingAmount;
|
||||
return `${width}px`;
|
||||
}
|
||||
return `${textLengthThreshold}ch`;
|
||||
default:
|
||||
return '1ch';
|
||||
}
|
||||
};
|
||||
private readonly _calculateFontSize = (phase: ScalingInputPhase): number => {
|
||||
return ScalingInput.calculateFontSizeFromProps(this.props, phase);
|
||||
};
|
||||
private readonly _getInputWidthInPx = (): number => {
|
||||
const ref = this._inputRef.current;
|
||||
if (!ref) {
|
||||
return 0;
|
||||
}
|
||||
return ref.getBoundingClientRect().width;
|
||||
};
|
||||
}
|
||||
29
packages/instant/src/components/search_input.tsx
Normal file
29
packages/instant/src/components/search_input.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import * as _ from 'lodash';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ColorOption } from '../style/theme';
|
||||
|
||||
import { Container } from './ui/container';
|
||||
import { Flex } from './ui/flex';
|
||||
import { Icon } from './ui/icon';
|
||||
import { Input, InputProps } from './ui/input';
|
||||
|
||||
export interface SearchInputProps extends InputProps {
|
||||
backgroundColor?: ColorOption;
|
||||
}
|
||||
|
||||
export const SearchInput: React.StatelessComponent<SearchInputProps> = props => (
|
||||
<Container backgroundColor={props.backgroundColor} borderRadius="3px" padding=".5em .3em">
|
||||
<Flex justify="flex-start" align="flex-end">
|
||||
<Icon width={14} height={14} icon="search" color={ColorOption.lightGrey} padding="0px 12px" />
|
||||
<Input {...props} fontSize="14px" fontColor={props.fontColor} />
|
||||
</Flex>
|
||||
</Container>
|
||||
);
|
||||
|
||||
SearchInput.displayName = 'SearchInput';
|
||||
|
||||
SearchInput.defaultProps = {
|
||||
backgroundColor: ColorOption.lightestGrey,
|
||||
fontColor: ColorOption.grey,
|
||||
};
|
||||
@@ -4,7 +4,6 @@ import * as React from 'react';
|
||||
import { ColorOption } from '../style/theme';
|
||||
|
||||
import { Button, ButtonProps } from './ui/button';
|
||||
import { Text } from './ui/text';
|
||||
|
||||
export interface SecondaryButtonProps extends ButtonProps {}
|
||||
|
||||
@@ -14,13 +13,16 @@ export const SecondaryButton: React.StatelessComponent<SecondaryButtonProps> = p
|
||||
<Button
|
||||
backgroundColor={ColorOption.white}
|
||||
borderColor={ColorOption.lightGrey}
|
||||
width="100%"
|
||||
width={props.width}
|
||||
onClick={props.onClick}
|
||||
fontColor={ColorOption.primaryColor}
|
||||
fontSize="16px"
|
||||
{...buttonProps}
|
||||
>
|
||||
<Text fontColor={ColorOption.primaryColor} fontWeight={600} fontSize="16px">
|
||||
{props.children}
|
||||
</Text>
|
||||
{props.children}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
SecondaryButton.defaultProps = {
|
||||
width: '100%',
|
||||
};
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { ScreenSpecification } from '../style/media';
|
||||
import { ColorOption } from '../style/theme';
|
||||
import { zIndex } from '../style/z_index';
|
||||
|
||||
import { SlideDownAnimation, SlideUpAnimation } from './animations/slide_animations';
|
||||
import { PositionAnimationSettings } from './animations/position_animation';
|
||||
import { SlideAnimation, SlideAnimationState } from './animations/slide_animation';
|
||||
|
||||
import { Container, Text } from './ui';
|
||||
import { Container } from './ui/container';
|
||||
import { Flex } from './ui/flex';
|
||||
import { Text } from './ui/text';
|
||||
|
||||
export interface ErrorProps {
|
||||
icon: string;
|
||||
@@ -18,27 +23,73 @@ export const Error: React.StatelessComponent<ErrorProps> = props => (
|
||||
backgroundColor={ColorOption.lightOrange}
|
||||
width="100%"
|
||||
borderRadius="6px"
|
||||
marginTop="10px"
|
||||
marginBottom="10px"
|
||||
>
|
||||
<Container marginRight="5px" display="inline" top="3px" position="relative">
|
||||
<Text fontSize="20px">{props.icon}</Text>
|
||||
</Container>
|
||||
<Text fontWeight="500" fontColor={ColorOption.darkOrange}>
|
||||
{props.message}
|
||||
</Text>
|
||||
<Flex justify="flex-start">
|
||||
<Container marginRight="5px" display="inline" top="3px" position="relative">
|
||||
<Text fontSize="20px">{props.icon}</Text>
|
||||
</Container>
|
||||
<Text fontWeight="500" fontColor={ColorOption.darkOrange}>
|
||||
{props.message}
|
||||
</Text>
|
||||
</Flex>
|
||||
</Container>
|
||||
);
|
||||
|
||||
export type SlidingDirection = 'up' | 'down';
|
||||
export interface SlidingErrorProps extends ErrorProps {
|
||||
direction: SlidingDirection;
|
||||
animationState: SlideAnimationState;
|
||||
}
|
||||
export const SlidingError: React.StatelessComponent<SlidingErrorProps> = props => {
|
||||
const AnimationComponent = props.direction === 'up' ? SlideUpAnimation : SlideDownAnimation;
|
||||
const slideAmount = '120px';
|
||||
|
||||
const desktopSlideIn: PositionAnimationSettings = {
|
||||
timingFunction: 'ease-in',
|
||||
top: {
|
||||
from: slideAmount,
|
||||
to: '0px',
|
||||
},
|
||||
position: 'relative',
|
||||
};
|
||||
const desktopSlideOut: PositionAnimationSettings = {
|
||||
timingFunction: 'cubic-bezier(0.25, 0.1, 0.25, 1)',
|
||||
top: {
|
||||
from: '0px',
|
||||
to: slideAmount,
|
||||
},
|
||||
position: 'relative',
|
||||
};
|
||||
|
||||
const mobileSlideIn: PositionAnimationSettings = {
|
||||
duration: '0.5s',
|
||||
timingFunction: 'ease-in',
|
||||
top: { from: '-120px', to: '0px' },
|
||||
position: 'fixed',
|
||||
};
|
||||
const moblieSlideOut: PositionAnimationSettings = {
|
||||
duration: '0.5s',
|
||||
timingFunction: 'ease-in',
|
||||
top: { from: '0px', to: '-120px' },
|
||||
position: 'fixed',
|
||||
};
|
||||
|
||||
const slideUpSettings: ScreenSpecification<PositionAnimationSettings> = {
|
||||
default: desktopSlideIn,
|
||||
sm: mobileSlideIn,
|
||||
};
|
||||
const slideOutSettings: ScreenSpecification<PositionAnimationSettings> = {
|
||||
default: desktopSlideOut,
|
||||
sm: moblieSlideOut,
|
||||
};
|
||||
|
||||
return (
|
||||
<AnimationComponent downY="120px">
|
||||
<SlideAnimation
|
||||
slideInSettings={slideUpSettings}
|
||||
slideOutSettings={slideOutSettings}
|
||||
zIndex={{ sm: zIndex.errorPopup, default: zIndex.errorPopBehind }}
|
||||
animationState={props.animationState}
|
||||
>
|
||||
<Error icon={props.icon} message={props.message} />
|
||||
</AnimationComponent>
|
||||
</SlideAnimation>
|
||||
);
|
||||
};
|
||||
|
||||
77
packages/instant/src/components/sliding_panel.tsx
Normal file
77
packages/instant/src/components/sliding_panel.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { ColorOption } from '../style/theme';
|
||||
import { zIndex } from '../style/z_index';
|
||||
|
||||
import { PositionAnimationSettings } from './animations/position_animation';
|
||||
import { SlideAnimation, SlideAnimationState } 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 }) => (
|
||||
<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>
|
||||
<Container marginTop="10px" height="100%">
|
||||
{children}
|
||||
</Container>
|
||||
</Container>
|
||||
);
|
||||
|
||||
export interface SlidingPanelProps extends PanelProps {
|
||||
animationState: SlideAnimationState;
|
||||
}
|
||||
|
||||
export const SlidingPanel: React.StatelessComponent<SlidingPanelProps> = props => {
|
||||
if (props.animationState === 'none') {
|
||||
return null;
|
||||
}
|
||||
const { animationState, ...rest } = props;
|
||||
const slideAmount = '100%';
|
||||
const slideUpSettings: PositionAnimationSettings = {
|
||||
duration: '0.3s',
|
||||
timingFunction: 'ease-in-out',
|
||||
top: {
|
||||
from: slideAmount,
|
||||
to: '0px',
|
||||
},
|
||||
position: 'absolute',
|
||||
};
|
||||
const slideDownSettings: PositionAnimationSettings = {
|
||||
duration: '0.3s',
|
||||
timingFunction: 'ease-out',
|
||||
top: {
|
||||
from: '0px',
|
||||
to: slideAmount,
|
||||
},
|
||||
position: 'absolute',
|
||||
};
|
||||
return (
|
||||
<SlideAnimation
|
||||
slideInSettings={slideUpSettings}
|
||||
slideOutSettings={slideDownSettings}
|
||||
animationState={animationState}
|
||||
height="100%"
|
||||
>
|
||||
<Panel {...rest} />
|
||||
</SlideAnimation>
|
||||
);
|
||||
};
|
||||
78
packages/instant/src/components/time_counter.tsx
Normal file
78
packages/instant/src/components/time_counter.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { ONE_SECOND_MS } from '../constants';
|
||||
import { ColorOption } from '../style/theme';
|
||||
import { timeUtil } from '../util/time';
|
||||
|
||||
import { Container } from './ui/container';
|
||||
import { Flex } from './ui/flex';
|
||||
import { Text } from './ui/text';
|
||||
|
||||
export interface TimeCounterProps {
|
||||
estimatedTimeMs: number;
|
||||
hasEnded: boolean;
|
||||
}
|
||||
interface TimeCounterState {
|
||||
elapsedSeconds: number;
|
||||
}
|
||||
|
||||
export class TimeCounter extends React.Component<TimeCounterProps, TimeCounterState> {
|
||||
public state = {
|
||||
elapsedSeconds: 0,
|
||||
};
|
||||
private _timerId?: number;
|
||||
|
||||
public componentDidMount(): void {
|
||||
this._setupTimerBasedOnProps();
|
||||
}
|
||||
|
||||
public componentWillUnmount(): void {
|
||||
this._clearTimer();
|
||||
}
|
||||
|
||||
public componentDidUpdate(prevProps: TimeCounterProps): void {
|
||||
if (prevProps.hasEnded !== this.props.hasEnded) {
|
||||
this._setupTimerBasedOnProps();
|
||||
}
|
||||
}
|
||||
|
||||
public render(): React.ReactNode {
|
||||
const estimatedTimeSeconds = this.props.estimatedTimeMs / ONE_SECOND_MS;
|
||||
return (
|
||||
<Flex justify="space-between">
|
||||
<Container>
|
||||
<Container marginRight="5px" display="inline">
|
||||
<Text fontWeight={600} fontColor={ColorOption.grey}>
|
||||
Est. Time
|
||||
</Text>
|
||||
</Container>
|
||||
<Text fontColor={ColorOption.grey}>
|
||||
({timeUtil.secondsToHumanDescription(estimatedTimeSeconds)})
|
||||
</Text>
|
||||
</Container>
|
||||
<Text fontColor={ColorOption.grey}>
|
||||
Time: {timeUtil.secondsToStopwatchTime(this.state.elapsedSeconds)}
|
||||
</Text>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
private _setupTimerBasedOnProps(): void {
|
||||
this.props.hasEnded ? this._clearTimer() : this._newTimer();
|
||||
}
|
||||
|
||||
private _newTimer(): void {
|
||||
this._clearTimer();
|
||||
this._timerId = window.setInterval(() => {
|
||||
this.setState({
|
||||
elapsedSeconds: this.state.elapsedSeconds + 1,
|
||||
});
|
||||
}, ONE_SECOND_MS);
|
||||
}
|
||||
|
||||
private _clearTimer(): void {
|
||||
if (this._timerId) {
|
||||
window.clearInterval(this._timerId);
|
||||
}
|
||||
}
|
||||
}
|
||||
80
packages/instant/src/components/timed_progress_bar.tsx
Normal file
80
packages/instant/src/components/timed_progress_bar.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
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 { Container } from './ui/container';
|
||||
|
||||
export interface TimedProgressBarProps {
|
||||
expectedTimeMs: number;
|
||||
hasEnded: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Timed Progress Bar
|
||||
* Goes from 0% -> PROGRESS_STALL_AT_WIDTH over time of expectedTimeMs
|
||||
* When hasEnded set to true, goes to 100% through animation of PROGRESS_FINISH_ANIMATION_TIME_MS length of time
|
||||
*/
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
private _calculateTimedProgressProps(): TimedProgressProps {
|
||||
if (this.props.hasEnded) {
|
||||
if (!this._barRef.current) {
|
||||
throw new Error('ended but no reference');
|
||||
}
|
||||
const fromWidth = `${this._barRef.current.offsetWidth}px`;
|
||||
return {
|
||||
timeMs: PROGRESS_FINISH_ANIMATION_TIME_MS,
|
||||
fromWidth,
|
||||
toWidth: '100%',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
timeMs: this.props.expectedTimeMs,
|
||||
fromWidth: '0px',
|
||||
toWidth: PROGRESS_STALL_AT_WIDTH,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const expandingWidthKeyframes = (fromWidth: string, toWidth: string) => {
|
||||
return keyframes`
|
||||
from {
|
||||
width: ${fromWidth};
|
||||
}
|
||||
to {
|
||||
width: ${toWidth};
|
||||
}
|
||||
`;
|
||||
};
|
||||
|
||||
interface TimedProgressProps {
|
||||
timeMs: number;
|
||||
fromWidth: string;
|
||||
toWidth: string;
|
||||
}
|
||||
|
||||
export const TimedProgress =
|
||||
styled.div <
|
||||
TimedProgressProps >
|
||||
`
|
||||
&& {
|
||||
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;
|
||||
}
|
||||
`;
|
||||
@@ -6,6 +6,8 @@ import { ColorOption, styled } from '../../style/theme';
|
||||
export interface ButtonProps {
|
||||
backgroundColor?: ColorOption;
|
||||
borderColor?: ColorOption;
|
||||
fontColor?: ColorOption;
|
||||
fontSize?: string;
|
||||
width?: string;
|
||||
padding?: string;
|
||||
type?: string;
|
||||
@@ -24,37 +26,49 @@ const darkenOnHoverAmount = 0.1;
|
||||
const darkenOnActiveAmount = 0.2;
|
||||
const saturateOnFocusAmount = 0.2;
|
||||
export const Button = styled(PlainButton)`
|
||||
cursor: ${props => (props.isDisabled ? 'default' : 'pointer')};
|
||||
transition: background-color, opacity 0.5s ease;
|
||||
padding: ${props => props.padding};
|
||||
border-radius: 3px;
|
||||
outline: none;
|
||||
width: ${props => props.width};
|
||||
background-color: ${props => (props.backgroundColor ? props.theme[props.backgroundColor] : 'none')};
|
||||
border: ${props => (props.borderColor ? `1px solid ${props.theme[props.borderColor]}` : 'none')};
|
||||
&:hover {
|
||||
background-color: ${props =>
|
||||
!props.isDisabled
|
||||
? darken(darkenOnHoverAmount, props.theme[props.backgroundColor || 'white'])
|
||||
: ''} !important;
|
||||
}
|
||||
&:active {
|
||||
background-color: ${props =>
|
||||
!props.isDisabled ? darken(darkenOnActiveAmount, props.theme[props.backgroundColor || 'white']) : ''};
|
||||
}
|
||||
&:disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
&:focus {
|
||||
background-color: ${props => saturate(saturateOnFocusAmount, props.theme[props.backgroundColor || 'white'])};
|
||||
&& {
|
||||
all: initial;
|
||||
box-sizing: border-box;
|
||||
font-size: ${props => props.fontSize};
|
||||
font-family: 'Inter UI', sans-serif;
|
||||
font-weight: 600;
|
||||
color: ${props => props.fontColor && props.theme[props.fontColor]};
|
||||
cursor: ${props => (props.isDisabled ? 'default' : 'pointer')};
|
||||
transition: background-color, opacity 0.5s ease;
|
||||
padding: ${props => props.padding};
|
||||
border-radius: 3px;
|
||||
text-align: center;
|
||||
outline: none;
|
||||
width: ${props => props.width};
|
||||
background-color: ${props => (props.backgroundColor ? props.theme[props.backgroundColor] : 'none')};
|
||||
border: ${props => (props.borderColor ? `1px solid ${props.theme[props.borderColor]}` : 'none')};
|
||||
&:hover {
|
||||
background-color: ${props =>
|
||||
!props.isDisabled
|
||||
? darken(darkenOnHoverAmount, props.theme[props.backgroundColor || 'white'])
|
||||
: ''} !important;
|
||||
}
|
||||
&:active {
|
||||
background-color: ${props =>
|
||||
!props.isDisabled ? darken(darkenOnActiveAmount, props.theme[props.backgroundColor || 'white']) : ''};
|
||||
}
|
||||
&:disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
&:focus {
|
||||
background-color: ${props =>
|
||||
saturate(saturateOnFocusAmount, props.theme[props.backgroundColor || 'white'])};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
Button.defaultProps = {
|
||||
backgroundColor: ColorOption.primaryColor,
|
||||
borderColor: ColorOption.primaryColor,
|
||||
width: 'auto',
|
||||
isDisabled: false,
|
||||
padding: '1em 2.2em',
|
||||
padding: '.6em 1.2em',
|
||||
fontSize: '15px',
|
||||
};
|
||||
|
||||
Button.displayName = 'Button';
|
||||
|
||||
27
packages/instant/src/components/ui/circle.tsx
Normal file
27
packages/instant/src/components/ui/circle.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import { ColorOption, styled, Theme, withTheme } from '../../style/theme';
|
||||
|
||||
export interface CircleProps {
|
||||
diameter: number;
|
||||
rawColor?: string;
|
||||
color?: ColorOption;
|
||||
theme: Theme;
|
||||
}
|
||||
|
||||
export const Circle = withTheme(
|
||||
styled.div <
|
||||
CircleProps >
|
||||
`
|
||||
&& {
|
||||
width: ${props => props.diameter}px;
|
||||
height: ${props => props.diameter}px;
|
||||
background-color: ${props => (props.rawColor ? props.rawColor : props.theme[props.color || ColorOption.white])};
|
||||
border-radius: 50%;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
Circle.displayName = 'Circle';
|
||||
|
||||
Circle.defaultProps = {
|
||||
color: ColorOption.white,
|
||||
};
|
||||
@@ -1,16 +1,18 @@
|
||||
import * as React from 'react';
|
||||
import { darken } from 'polished';
|
||||
|
||||
import { MediaChoice, stylesForMedia } from '../../style/media';
|
||||
import { ColorOption, styled } from '../../style/theme';
|
||||
import { cssRuleIfExists } from '../../style/util';
|
||||
|
||||
export interface ContainerProps {
|
||||
display?: string;
|
||||
display?: MediaChoice;
|
||||
position?: string;
|
||||
top?: string;
|
||||
right?: string;
|
||||
bottom?: string;
|
||||
left?: string;
|
||||
width?: string;
|
||||
width?: MediaChoice;
|
||||
height?: MediaChoice;
|
||||
maxWidth?: string;
|
||||
margin?: string;
|
||||
marginTop?: string;
|
||||
@@ -27,38 +29,59 @@ export interface ContainerProps {
|
||||
backgroundColor?: ColorOption;
|
||||
hasBoxShadow?: boolean;
|
||||
zIndex?: number;
|
||||
whiteSpace?: string;
|
||||
opacity?: number;
|
||||
cursor?: string;
|
||||
overflow?: string;
|
||||
darkenOnHover?: boolean;
|
||||
boxShadowOnHover?: boolean;
|
||||
flexGrow?: string | number;
|
||||
}
|
||||
|
||||
const PlainContainer: React.StatelessComponent<ContainerProps> = ({ children, className }) => (
|
||||
<div className={className}>{children}</div>
|
||||
);
|
||||
|
||||
export const Container = styled(PlainContainer)`
|
||||
box-sizing: border-box;
|
||||
${props => cssRuleIfExists(props, 'display')}
|
||||
${props => cssRuleIfExists(props, 'position')}
|
||||
${props => cssRuleIfExists(props, 'top')}
|
||||
${props => cssRuleIfExists(props, 'right')}
|
||||
${props => cssRuleIfExists(props, 'bottom')}
|
||||
${props => cssRuleIfExists(props, 'left')}
|
||||
${props => cssRuleIfExists(props, 'width')}
|
||||
${props => cssRuleIfExists(props, 'max-width')}
|
||||
${props => cssRuleIfExists(props, 'margin')}
|
||||
${props => cssRuleIfExists(props, 'margin-top')}
|
||||
${props => cssRuleIfExists(props, 'margin-right')}
|
||||
${props => cssRuleIfExists(props, 'margin-bottom')}
|
||||
${props => cssRuleIfExists(props, 'margin-left')}
|
||||
${props => cssRuleIfExists(props, 'padding')}
|
||||
${props => cssRuleIfExists(props, 'border-radius')}
|
||||
${props => cssRuleIfExists(props, 'border')}
|
||||
${props => cssRuleIfExists(props, 'border-top')}
|
||||
${props => cssRuleIfExists(props, 'border-bottom')}
|
||||
${props => cssRuleIfExists(props, 'z-index')}
|
||||
${props => cssRuleIfExists(props, 'opacity')}
|
||||
${props => (props.hasBoxShadow ? `box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1)` : '')};
|
||||
background-color: ${props => (props.backgroundColor ? props.theme[props.backgroundColor] : 'none')};
|
||||
border-color: ${props => (props.borderColor ? props.theme[props.borderColor] : 'none')};
|
||||
export const Container =
|
||||
styled.div <
|
||||
ContainerProps >
|
||||
`
|
||||
&& {
|
||||
box-sizing: border-box;
|
||||
${props => cssRuleIfExists(props, 'flex-grow')}
|
||||
${props => cssRuleIfExists(props, 'position')}
|
||||
${props => cssRuleIfExists(props, 'top')}
|
||||
${props => cssRuleIfExists(props, 'right')}
|
||||
${props => cssRuleIfExists(props, 'bottom')}
|
||||
${props => cssRuleIfExists(props, 'left')}
|
||||
${props => cssRuleIfExists(props, 'max-width')}
|
||||
${props => cssRuleIfExists(props, 'margin')}
|
||||
${props => cssRuleIfExists(props, 'margin-top')}
|
||||
${props => cssRuleIfExists(props, 'margin-right')}
|
||||
${props => cssRuleIfExists(props, 'margin-bottom')}
|
||||
${props => cssRuleIfExists(props, 'margin-left')}
|
||||
${props => cssRuleIfExists(props, 'padding')}
|
||||
${props => cssRuleIfExists(props, 'border-radius')}
|
||||
${props => cssRuleIfExists(props, 'border')}
|
||||
${props => cssRuleIfExists(props, 'border-top')}
|
||||
${props => cssRuleIfExists(props, 'border-bottom')}
|
||||
${props => cssRuleIfExists(props, 'z-index')}
|
||||
${props => cssRuleIfExists(props, 'white-space')}
|
||||
${props => cssRuleIfExists(props, 'opacity')}
|
||||
${props => cssRuleIfExists(props, 'cursor')}
|
||||
${props => cssRuleIfExists(props, 'overflow')}
|
||||
${props => (props.hasBoxShadow ? `box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1)` : '')};
|
||||
${props => props.display && stylesForMedia<string>('display', props.display)}
|
||||
${props => props.width && stylesForMedia<string>('width', props.width)}
|
||||
${props => props.height && stylesForMedia<string>('height', props.height)}
|
||||
background-color: ${props => (props.backgroundColor ? props.theme[props.backgroundColor] : 'none')};
|
||||
border-color: ${props => (props.borderColor ? props.theme[props.borderColor] : 'none')};
|
||||
&:hover {
|
||||
${props =>
|
||||
props.darkenOnHover
|
||||
? `background-color: ${
|
||||
props.backgroundColor ? darken(0.05, props.theme[props.backgroundColor]) : 'none'
|
||||
}`
|
||||
: ''};
|
||||
${props => (props.boxShadowOnHover ? 'box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1)' : '')};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
Container.defaultProps = {
|
||||
|
||||
134
packages/instant/src/components/ui/dropdown.tsx
Normal file
134
packages/instant/src/components/ui/dropdown.tsx
Normal file
@@ -0,0 +1,134 @@
|
||||
import * as _ from 'lodash';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ColorOption, completelyTransparent } from '../../style/theme';
|
||||
import { zIndex } from '../../style/z_index';
|
||||
|
||||
import { Container } from './container';
|
||||
import { Flex } from './flex';
|
||||
import { Icon } from './icon';
|
||||
import { Overlay } from './overlay';
|
||||
import { Text } from './text';
|
||||
|
||||
export interface DropdownItemConfig {
|
||||
text: string;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
export interface DropdownProps {
|
||||
value: string;
|
||||
label?: string;
|
||||
items: DropdownItemConfig[];
|
||||
}
|
||||
|
||||
export interface DropdownState {
|
||||
isOpen: boolean;
|
||||
}
|
||||
|
||||
export class Dropdown extends React.Component<DropdownProps, DropdownState> {
|
||||
public static defaultProps = {
|
||||
items: [],
|
||||
};
|
||||
public state: DropdownState = {
|
||||
isOpen: false,
|
||||
};
|
||||
public render(): React.ReactNode {
|
||||
const { value, label, items } = this.props;
|
||||
const { isOpen } = this.state;
|
||||
const hasItems = !_.isEmpty(items);
|
||||
const borderRadius = isOpen ? '4px 4px 0px 0px' : '4px';
|
||||
return (
|
||||
<React.Fragment>
|
||||
{isOpen && (
|
||||
<Overlay
|
||||
zIndex={zIndex.dropdownItems - 1}
|
||||
backgroundColor={completelyTransparent}
|
||||
onClick={this._closeDropdown}
|
||||
/>
|
||||
)}
|
||||
<Container position="relative">
|
||||
<Container
|
||||
cursor={hasItems ? 'pointer' : undefined}
|
||||
onClick={this._handleDropdownClick}
|
||||
hasBoxShadow={isOpen}
|
||||
boxShadowOnHover={true}
|
||||
borderRadius={borderRadius}
|
||||
border="1px solid"
|
||||
borderColor={ColorOption.feintGrey}
|
||||
padding="0.8em"
|
||||
>
|
||||
<Flex justify="space-between">
|
||||
<Text fontSize="16px" fontColor={ColorOption.darkGrey}>
|
||||
{value}
|
||||
</Text>
|
||||
<Container>
|
||||
{label && (
|
||||
<Text fontSize="16px" fontColor={ColorOption.lightGrey}>
|
||||
{label}
|
||||
</Text>
|
||||
)}
|
||||
{hasItems && (
|
||||
<Container marginLeft="5px" display="inline-block" position="relative" bottom="2px">
|
||||
<Icon padding="3px" icon="chevron" width={12} stroke={ColorOption.grey} />
|
||||
</Container>
|
||||
)}
|
||||
</Container>
|
||||
</Flex>
|
||||
</Container>
|
||||
{isOpen && (
|
||||
<Container
|
||||
width="100%"
|
||||
position="absolute"
|
||||
onClick={this._closeDropdown}
|
||||
backgroundColor={ColorOption.white}
|
||||
hasBoxShadow={true}
|
||||
zIndex={zIndex.dropdownItems}
|
||||
>
|
||||
{_.map(items, (item, index) => (
|
||||
<DropdownItem key={item.text} {...item} isLast={index === items.length - 1} />
|
||||
))}
|
||||
</Container>
|
||||
)}
|
||||
</Container>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
private readonly _handleDropdownClick = (): void => {
|
||||
if (_.isEmpty(this.props.items)) {
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
isOpen: !this.state.isOpen,
|
||||
});
|
||||
};
|
||||
private readonly _closeDropdown = (): void => {
|
||||
this.setState({
|
||||
isOpen: false,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export interface DropdownItemProps extends DropdownItemConfig {
|
||||
text: string;
|
||||
onClick?: () => void;
|
||||
isLast: boolean;
|
||||
}
|
||||
|
||||
export const DropdownItem: React.StatelessComponent<DropdownItemProps> = ({ text, onClick, isLast }) => (
|
||||
<Container
|
||||
onClick={onClick}
|
||||
cursor="pointer"
|
||||
darkenOnHover={true}
|
||||
backgroundColor={ColorOption.white}
|
||||
padding="0.8em"
|
||||
borderTop="0"
|
||||
border="1px solid"
|
||||
borderRadius={isLast ? '0px 0px 4px 4px' : undefined}
|
||||
width="100%"
|
||||
borderColor={ColorOption.feintGrey}
|
||||
>
|
||||
<Text fontSize="14px" fontColor={ColorOption.darkGrey}>
|
||||
{text}
|
||||
</Text>
|
||||
</Container>
|
||||
);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user