merge development
This commit is contained in:
commit
cdd650d0eb
@ -98,6 +98,7 @@ jobs:
|
|||||||
- run: yarn wsrun test:circleci @0xproject/subproviders
|
- run: yarn wsrun test:circleci @0xproject/subproviders
|
||||||
- run: yarn wsrun test:circleci @0xproject/web3-wrapper
|
- run: yarn wsrun test:circleci @0xproject/web3-wrapper
|
||||||
- run: yarn wsrun test:circleci @0xproject/utils
|
- run: yarn wsrun test:circleci @0xproject/utils
|
||||||
|
- run: yarn wsrun test:circleci @0xproject/instant
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: coverage-abi-gen-{{ .Environment.CIRCLE_SHA1 }}
|
key: coverage-abi-gen-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
paths:
|
paths:
|
||||||
|
@ -4,6 +4,7 @@ lib
|
|||||||
/packages/contracts/generated-artifacts
|
/packages/contracts/generated-artifacts
|
||||||
/packages/abi-gen-wrappers/src/generated-wrappers
|
/packages/abi-gen-wrappers/src/generated-wrappers
|
||||||
/packages/contract-artifacts/artifacts
|
/packages/contract-artifacts/artifacts
|
||||||
|
/packages/json-schemas/schemas
|
||||||
/packages/metacoin/src/contract_wrappers
|
/packages/metacoin/src/contract_wrappers
|
||||||
/packages/metacoin/artifacts
|
/packages/metacoin/artifacts
|
||||||
/packages/sra-spec/public/
|
/packages/sra-spec/public/
|
||||||
|
18
CODEOWNERS
18
CODEOWNERS
@ -8,3 +8,21 @@
|
|||||||
packages/asset-buyer/ @BMillman19 @fragosti @steveklebanoff
|
packages/asset-buyer/ @BMillman19 @fragosti @steveklebanoff
|
||||||
packages/instant/ @BMillman19 @fragosti @steveklebanoff
|
packages/instant/ @BMillman19 @fragosti @steveklebanoff
|
||||||
packages/website/ @BMillman19 @fragosti @fabioberger @steveklebanoff
|
packages/website/ @BMillman19 @fragosti @fabioberger @steveklebanoff
|
||||||
|
|
||||||
|
# Dev tools & setup
|
||||||
|
packages/abi-gen/ @LogvinovLeon
|
||||||
|
packages/base-contract/ @LogvinovLeon
|
||||||
|
packages/contract_templates/ @LogvinovLeon
|
||||||
|
packages/dev-utils/ @LogvinovLeon @fabioberger
|
||||||
|
|
||||||
|
packages/ethereum-types/ @LogvinovLeon
|
||||||
|
packages/metacoin/ @LogvinovLeon
|
||||||
|
packages/sol-compiler/ @LogvinovLeon
|
||||||
|
packages/sol-cov/ @LogvinovLeon
|
||||||
|
packages/sol-resolver/ @LogvinovLeon
|
||||||
|
packages/web3-wrapper/ @LogvinovLeon @fabioberger
|
||||||
|
.circleci/ @LogvinovLeon
|
||||||
|
packages/subproviders/ @fabioberger @dekz
|
||||||
|
packages/connect/ @fragosti
|
||||||
|
packages/monorepo-scripts/ @fabioberger
|
||||||
|
packages/order-utils/ @fabioberger @LogvinovLeon
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"mnemonic": "concert load couple harbor equip island argue ramp clarify fence smart topic",
|
"mnemonic": "concert load couple harbor equip island argue ramp clarify fence smart topic",
|
||||||
"packagesWithDocPages": "0x.js connect json-schemas subproviders web3-wrapper contract-wrappers order-utils order-watcher sol-compiler sol-cov ethereum-types"
|
"packagesWithDocPages": "0x.js connect json-schemas subproviders web3-wrapper contract-wrappers order-utils order-watcher sol-compiler sol-cov ethereum-types asset-buyer"
|
||||||
},
|
},
|
||||||
"bundlewatch" : {
|
"bundlewatch" : {
|
||||||
"files": [
|
"files": [
|
||||||
@ -50,7 +50,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "packages/instant/public/main.bundle.js",
|
"path": "packages/instant/public/main.bundle.js",
|
||||||
"maxSize": "350kB"
|
"maxSize": "500kB"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"ci": {
|
"ci": {
|
||||||
@ -65,7 +65,7 @@
|
|||||||
"coveralls": "^3.0.0",
|
"coveralls": "^3.0.0",
|
||||||
"ganache-cli": "6.1.8",
|
"ganache-cli": "6.1.8",
|
||||||
"lcov-result-merger": "^3.0.0",
|
"lcov-result-merger": "^3.0.0",
|
||||||
"npm-cli-login": "^0.0.10",
|
"@0xproject/npm-cli-login": "^0.0.11",
|
||||||
"npm-run-all": "^4.1.2",
|
"npm-run-all": "^4.1.2",
|
||||||
"prettier": "^1.11.1",
|
"prettier": "^1.11.1",
|
||||||
"source-map-support": "^0.5.6",
|
"source-map-support": "^0.5.6",
|
||||||
|
@ -24,6 +24,10 @@
|
|||||||
{
|
{
|
||||||
"note": "Make web3-provider-engine types a 'dependency' so it's available to users of the library",
|
"note": "Make web3-provider-engine types a 'dependency' so it's available to users of the library",
|
||||||
"pr": 1105
|
"pr": 1105
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Export new `AssetData` type from types",
|
||||||
|
"pr": 1131
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -68,7 +68,7 @@
|
|||||||
"sinon": "^4.0.0",
|
"sinon": "^4.0.0",
|
||||||
"source-map-support": "^0.5.0",
|
"source-map-support": "^0.5.0",
|
||||||
"tslint": "5.11.0",
|
"tslint": "5.11.0",
|
||||||
"typedoc": "0.12.0",
|
"typedoc": "0.13.0",
|
||||||
"typescript": "3.0.1",
|
"typescript": "3.0.1",
|
||||||
"uglifyjs-webpack-plugin": "^2.0.1",
|
"uglifyjs-webpack-plugin": "^2.0.1",
|
||||||
"webpack": "^4.20.2"
|
"webpack": "^4.20.2"
|
||||||
|
@ -79,6 +79,7 @@ export {
|
|||||||
OrderStateInvalid,
|
OrderStateInvalid,
|
||||||
OrderState,
|
OrderState,
|
||||||
AssetProxyId,
|
AssetProxyId,
|
||||||
|
AssetData,
|
||||||
ERC20AssetData,
|
ERC20AssetData,
|
||||||
ERC721AssetData,
|
ERC721AssetData,
|
||||||
SignatureType,
|
SignatureType,
|
||||||
|
@ -5,10 +5,26 @@
|
|||||||
{
|
{
|
||||||
"note": "Add `gasLimit` and `gasPrice` as optional properties on `BuyQuoteExecutionOpts`"
|
"note": "Add `gasLimit` and `gasPrice` as optional properties on `BuyQuoteExecutionOpts`"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"note": "Export `BuyQuoteInfo` type",
|
||||||
|
"pr": 1131
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"note":
|
"note":
|
||||||
"Updated to use new modularized artifacts and the latest version of @0xproject/contract-wrappers",
|
"Updated to use new modularized artifacts and the latest version of @0xproject/contract-wrappers",
|
||||||
"pr": 1105
|
"pr": 1105
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add `gasLimit` and `gasPrice` as optional properties on `BuyQuoteExecutionOpts`",
|
||||||
|
"pr": 1116
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add `docs:json` command to package.json",
|
||||||
|
"pr": 1139
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add missing types to public interface",
|
||||||
|
"pr": 1139
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -17,7 +17,8 @@
|
|||||||
"coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info",
|
"coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info",
|
||||||
"test:circleci": "yarn test:coverage",
|
"test:circleci": "yarn test:coverage",
|
||||||
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js --exit",
|
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js --exit",
|
||||||
"clean": "shx rm -rf lib test_temp"
|
"clean": "shx rm -rf lib test_temp",
|
||||||
|
"docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"postpublish": {
|
"postpublish": {
|
||||||
@ -63,7 +64,7 @@
|
|||||||
"nyc": "^11.0.1",
|
"nyc": "^11.0.1",
|
||||||
"shx": "^0.2.2",
|
"shx": "^0.2.2",
|
||||||
"tslint": "5.11.0",
|
"tslint": "5.11.0",
|
||||||
"typedoc": "0.12.0",
|
"typedoc": "0.13.0",
|
||||||
"typescript": "3.0.1"
|
"typescript": "3.0.1"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
|
@ -1,4 +1,10 @@
|
|||||||
export { Provider } from 'ethereum-types';
|
export {
|
||||||
|
JSONRPCRequestPayload,
|
||||||
|
JSONRPCResponsePayload,
|
||||||
|
JSONRPCResponseError,
|
||||||
|
JSONRPCErrorCallback,
|
||||||
|
Provider,
|
||||||
|
} from 'ethereum-types';
|
||||||
export { SignedOrder } from '@0xproject/types';
|
export { SignedOrder } from '@0xproject/types';
|
||||||
export { BigNumber } from '@0xproject/utils';
|
export { BigNumber } from '@0xproject/utils';
|
||||||
|
|
||||||
@ -10,6 +16,7 @@ export {
|
|||||||
AssetBuyerOpts,
|
AssetBuyerOpts,
|
||||||
BuyQuote,
|
BuyQuote,
|
||||||
BuyQuoteExecutionOpts,
|
BuyQuoteExecutionOpts,
|
||||||
|
BuyQuoteInfo,
|
||||||
BuyQuoteRequestOpts,
|
BuyQuoteRequestOpts,
|
||||||
OrderProvider,
|
OrderProvider,
|
||||||
OrderProviderRequest,
|
OrderProviderRequest,
|
||||||
|
@ -129,7 +129,8 @@ export class BaseContract {
|
|||||||
if (abiDefinition.type !== AbiType.Function) {
|
if (abiDefinition.type !== AbiType.Function) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const abiFunctionSignature = abiUtils.getFunctionSignature(abiDefinition);
|
// tslint:disable-next-line:no-unnecessary-type-assertion
|
||||||
|
const abiFunctionSignature = abiUtils.getFunctionSignature(abiDefinition as MethodAbi);
|
||||||
if (abiFunctionSignature === functionSignature) {
|
if (abiFunctionSignature === functionSignature) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@
|
|||||||
"nyc": "^11.0.1",
|
"nyc": "^11.0.1",
|
||||||
"shx": "^0.2.2",
|
"shx": "^0.2.2",
|
||||||
"tslint": "5.11.0",
|
"tslint": "5.11.0",
|
||||||
"typedoc": "0.12.0",
|
"typedoc": "0.13.0",
|
||||||
"typescript": "3.0.1"
|
"typescript": "3.0.1"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
|
@ -14,7 +14,7 @@ export const assert = {
|
|||||||
sharedAssert.doesConformToSchema(
|
sharedAssert.doesConformToSchema(
|
||||||
variableName,
|
variableName,
|
||||||
subscriptionOpts,
|
subscriptionOpts,
|
||||||
schemas.relayerApiOrdersChannelSubscribePayload,
|
schemas.relayerApiOrdersChannelSubscribePayloadSchema,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
isOrdersChannelHandler(variableName: string, handler: any): void {
|
isOrdersChannelHandler(variableName: string, handler: any): void {
|
||||||
|
@ -1,11 +1,19 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"changes": [
|
"changes": [
|
||||||
{
|
{
|
||||||
"pr": 1105,
|
"pr": 1105,
|
||||||
"note": "Initial release"
|
"note": "Initial release"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Unpublished Package"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -1,11 +1,19 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"changes": [
|
"changes": [
|
||||||
{
|
{
|
||||||
"pr": 1105,
|
"pr": 1105,
|
||||||
"note": "Initial release"
|
"note": "Initial release"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Unpublished Package"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -60,7 +60,7 @@
|
|||||||
"sinon": "^4.0.0",
|
"sinon": "^4.0.0",
|
||||||
"source-map-support": "^0.5.0",
|
"source-map-support": "^0.5.0",
|
||||||
"tslint": "5.11.0",
|
"tslint": "5.11.0",
|
||||||
"typedoc": "0.12.0",
|
"typedoc": "0.13.0",
|
||||||
"typescript": "3.0.1",
|
"typescript": "3.0.1",
|
||||||
"web3-provider-engine": "14.0.6"
|
"web3-provider-engine": "14.0.6"
|
||||||
},
|
},
|
||||||
|
@ -20,7 +20,9 @@ export type ConstructorStateMutability = 'nonpayable' | 'payable';
|
|||||||
export type StateMutability = 'pure' | 'view' | ConstructorStateMutability;
|
export type StateMutability = 'pure' | 'view' | ConstructorStateMutability;
|
||||||
|
|
||||||
export interface MethodAbi {
|
export interface MethodAbi {
|
||||||
type: 'function'; // We hardcode this here b/c doc pages cannot render an enum value
|
// Ideally this would be set to: `'function'` but then TS complains when artifacts are loaded
|
||||||
|
// from JSON files, and this value has type `string` not type `'function'`
|
||||||
|
type: string;
|
||||||
name: string;
|
name: string;
|
||||||
inputs: DataItem[];
|
inputs: DataItem[];
|
||||||
outputs: DataItem[];
|
outputs: DataItem[];
|
||||||
@ -30,14 +32,18 @@ export interface MethodAbi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ConstructorAbi {
|
export interface ConstructorAbi {
|
||||||
type: 'constructor'; // We hardcode this here b/c doc pages cannot render an enum value
|
// Ideally this would be set to: `'constructor'` but then TS complains when artifacts are loaded
|
||||||
|
// from JSON files, and this value has type `string` not type `'constructor'`
|
||||||
|
type: string;
|
||||||
inputs: DataItem[];
|
inputs: DataItem[];
|
||||||
payable: boolean;
|
payable: boolean;
|
||||||
stateMutability: ConstructorStateMutability;
|
stateMutability: ConstructorStateMutability;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FallbackAbi {
|
export interface FallbackAbi {
|
||||||
type: 'fallback'; // We hardcode this here b/c doc pages cannot render an enum value
|
// Ideally this would be set to: `'fallback'` but then TS complains when artifacts are loaded
|
||||||
|
// from JSON files, and this value has type `string` not type `'fallback'`
|
||||||
|
type: string;
|
||||||
payable: boolean;
|
payable: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +52,9 @@ export interface EventParameter extends DataItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface EventAbi {
|
export interface EventAbi {
|
||||||
type: 'event'; // We hardcode this here b/c doc pages cannot render an enum value
|
// Ideally this would be set to: `'event'` but then TS complains when artifacts are loaded
|
||||||
|
// from JSON files, and this value has type `string` not type `'event'`
|
||||||
|
type: string;
|
||||||
name: string;
|
name: string;
|
||||||
inputs: EventParameter[];
|
inputs: EventParameter[];
|
||||||
anonymous: boolean;
|
anonymous: boolean;
|
||||||
|
@ -55,12 +55,15 @@
|
|||||||
"react-dom": "^16.5.2",
|
"react-dom": "^16.5.2",
|
||||||
"react-redux": "^5.0.7",
|
"react-redux": "^5.0.7",
|
||||||
"redux": "^4.0.0",
|
"redux": "^4.0.0",
|
||||||
"styled-components": "^3.4.9"
|
"redux-devtools-extension": "^2.13.5",
|
||||||
|
"styled-components": "^3.4.9",
|
||||||
|
"ts-optchain": "^0.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0xproject/tslint-config": "^1.0.8",
|
"@0xproject/tslint-config": "^1.0.8",
|
||||||
"@types/enzyme": "^3.1.14",
|
"@types/enzyme": "^3.1.14",
|
||||||
"@types/enzyme-adapter-react-16": "^1.0.3",
|
"@types/enzyme-adapter-react-16": "^1.0.3",
|
||||||
|
"@types/jest": "^23.3.5",
|
||||||
"@types/lodash": "^4.14.116",
|
"@types/lodash": "^4.14.116",
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
"@types/react": "^16.4.16",
|
"@types/react": "^16.4.16",
|
||||||
@ -77,7 +80,7 @@
|
|||||||
"shx": "^0.2.2",
|
"shx": "^0.2.2",
|
||||||
"ts-jest": "^23.10.3",
|
"ts-jest": "^23.10.3",
|
||||||
"tslint": "5.11.0",
|
"tslint": "5.11.0",
|
||||||
"typedoc": "0.12.0",
|
"typedoc": "0.13.0",
|
||||||
"typescript": "3.0.1",
|
"typescript": "3.0.1",
|
||||||
"webpack": "^4.20.2",
|
"webpack": "^4.20.2",
|
||||||
"webpack-cli": "^3.1.1",
|
"webpack-cli": "^3.1.1",
|
||||||
|
@ -3,6 +3,7 @@ import * as _ from 'lodash';
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import { ColorOption } from '../style/theme';
|
import { ColorOption } from '../style/theme';
|
||||||
|
import { util } from '../util/util';
|
||||||
|
|
||||||
import { Container, Input } from './ui';
|
import { Container, Input } from './ui';
|
||||||
|
|
||||||
@ -10,10 +11,13 @@ export interface AmountInputProps {
|
|||||||
fontColor?: ColorOption;
|
fontColor?: ColorOption;
|
||||||
fontSize?: string;
|
fontSize?: string;
|
||||||
value?: BigNumber;
|
value?: BigNumber;
|
||||||
onChange?: (value?: BigNumber) => void;
|
onChange: (value?: BigNumber) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AmountInput extends React.Component<AmountInputProps> {
|
export class AmountInput extends React.Component<AmountInputProps> {
|
||||||
|
public static defaultProps = {
|
||||||
|
onChange: util.boundNoop,
|
||||||
|
};
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
const { fontColor, fontSize, value } = this.props;
|
const { fontColor, fontSize, value } = this.props;
|
||||||
return (
|
return (
|
||||||
@ -24,7 +28,7 @@ export class AmountInput extends React.Component<AmountInputProps> {
|
|||||||
onChange={this._handleChange}
|
onChange={this._handleChange}
|
||||||
value={!_.isUndefined(value) ? value.toString() : ''}
|
value={!_.isUndefined(value) ? value.toString() : ''}
|
||||||
placeholder="0.00"
|
placeholder="0.00"
|
||||||
width="2em"
|
width="2.2em"
|
||||||
/>
|
/>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
@ -40,8 +44,6 @@ export class AmountInput extends React.Component<AmountInputProps> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!_.isUndefined(this.props.onChange)) {
|
|
||||||
this.props.onChange(bigNumberValue);
|
this.props.onChange(bigNumberValue);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
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,96 +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 SlideUpAnimationComponent: React.StatelessComponent<SlideAnimationComponentProps> = props => (
|
|
||||||
<SlideAnimation animationType="ease-in" keyframes={slideKeyframeGenerator(props.downY, '0px')}>
|
|
||||||
{props.children}
|
|
||||||
</SlideAnimation>
|
|
||||||
);
|
|
||||||
|
|
||||||
export const SlideDownAnimationComponent: 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>
|
|
||||||
);
|
|
||||||
|
|
||||||
export interface SlideUpAndDownAnimationProps extends SlideAnimationComponentProps {
|
|
||||||
delayMs: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum SlideState {
|
|
||||||
Up = 'up',
|
|
||||||
Down = 'down',
|
|
||||||
}
|
|
||||||
interface SlideUpAndDownState {
|
|
||||||
slideState: SlideState;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SlideUpAndDownAnimation extends React.Component<SlideUpAndDownAnimationProps, SlideUpAndDownState> {
|
|
||||||
public state = {
|
|
||||||
slideState: SlideState.Up,
|
|
||||||
};
|
|
||||||
|
|
||||||
private _timeoutId?: number;
|
|
||||||
public render(): React.ReactNode {
|
|
||||||
return this._renderSlide();
|
|
||||||
}
|
|
||||||
public componentDidMount(): void {
|
|
||||||
this._timeoutId = window.setTimeout(() => {
|
|
||||||
this.setState({
|
|
||||||
slideState: SlideState.Down,
|
|
||||||
});
|
|
||||||
}, this.props.delayMs);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
public componentWillUnmount(): void {
|
|
||||||
if (this._timeoutId) {
|
|
||||||
window.clearTimeout(this._timeoutId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private _renderSlide(): React.ReactNode {
|
|
||||||
const SlideComponent = this.state.slideState === 'up' ? SlideUpAnimationComponent : SlideDownAnimationComponent;
|
|
||||||
|
|
||||||
return <SlideComponent downY={this.props.downY}>{this.props.children}</SlideComponent>;
|
|
||||||
}
|
|
||||||
}
|
|
38
packages/instant/src/components/asset_amount_input.tsx
Normal file
38
packages/instant/src/components/asset_amount_input.tsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { BigNumber } from '@0xproject/utils';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { assetDataUtil } from '../util/asset_data';
|
||||||
|
|
||||||
|
import { ColorOption } from '../style/theme';
|
||||||
|
import { util } from '../util/util';
|
||||||
|
|
||||||
|
import { AmountInput, AmountInputProps } from './amount_input';
|
||||||
|
import { Container, Text } from './ui';
|
||||||
|
|
||||||
|
export interface AssetAmountInputProps extends AmountInputProps {
|
||||||
|
assetData?: string;
|
||||||
|
onChange: (value?: BigNumber, assetData?: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AssetAmountInput extends React.Component<AssetAmountInputProps> {
|
||||||
|
public static defaultProps = {
|
||||||
|
onChange: util.boundNoop,
|
||||||
|
};
|
||||||
|
public render(): React.ReactNode {
|
||||||
|
const { assetData, 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">
|
||||||
|
{assetDataUtil.bestNameForAsset(this.props.assetData, '???')}
|
||||||
|
</Text>
|
||||||
|
</Container>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
private readonly _handleChange = (value?: BigNumber): void => {
|
||||||
|
this.props.onChange(value, this.props.assetData);
|
||||||
|
};
|
||||||
|
}
|
@ -1,19 +1,53 @@
|
|||||||
|
import { BuyQuote } from '@0xproject/asset-buyer';
|
||||||
|
import * as _ from 'lodash';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import { ColorOption } from '../style/theme';
|
import { ColorOption } from '../style/theme';
|
||||||
|
import { assetBuyer } from '../util/asset_buyer';
|
||||||
|
import { util } from '../util/util';
|
||||||
|
import { web3Wrapper } from '../util/web3_wrapper';
|
||||||
|
|
||||||
import { Button, Container, Text } from './ui';
|
import { Button, Container, Text } from './ui';
|
||||||
|
|
||||||
export interface BuyButtonProps {}
|
export interface BuyButtonProps {
|
||||||
|
buyQuote?: BuyQuote;
|
||||||
|
onClick: (buyQuote: BuyQuote) => void;
|
||||||
|
onBuySuccess: (buyQuote: BuyQuote, txnHash: string) => void;
|
||||||
|
onBuyFailure: (buyQuote: BuyQuote, tnxHash?: string) => void;
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
export const BuyButton: React.StatelessComponent<BuyButtonProps> = props => (
|
export class BuyButton extends React.Component<BuyButtonProps> {
|
||||||
|
public static defaultProps = {
|
||||||
|
onClick: util.boundNoop,
|
||||||
|
onBuySuccess: util.boundNoop,
|
||||||
|
onBuyFailure: util.boundNoop,
|
||||||
|
};
|
||||||
|
public render(): React.ReactNode {
|
||||||
|
const shouldDisableButton = _.isUndefined(this.props.buyQuote);
|
||||||
|
return (
|
||||||
<Container padding="20px" width="100%">
|
<Container padding="20px" width="100%">
|
||||||
<Button width="100%">
|
<Button width="100%" onClick={this._handleClick} isDisabled={shouldDisableButton}>
|
||||||
<Text fontColor={ColorOption.white} fontWeight={600} fontSize="20px">
|
<Text fontColor={ColorOption.white} fontWeight={600} fontSize="20px">
|
||||||
Buy
|
{this.props.text}
|
||||||
</Text>
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
BuyButton.displayName = 'BuyButton';
|
private readonly _handleClick = async () => {
|
||||||
|
// The button is disabled when there is no buy quote anyway.
|
||||||
|
if (_.isUndefined(this.props.buyQuote)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.props.onClick(this.props.buyQuote);
|
||||||
|
let txnHash;
|
||||||
|
try {
|
||||||
|
txnHash = await assetBuyer.executeBuyQuoteAsync(this.props.buyQuote);
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(txnHash);
|
||||||
|
this.props.onBuySuccess(this.props.buyQuote, txnHash);
|
||||||
|
} catch {
|
||||||
|
this.props.onBuyFailure(this.props.buyQuote, txnHash);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@ -1,11 +1,58 @@
|
|||||||
|
import { BigNumber } from '@0xproject/utils';
|
||||||
|
import * as _ from 'lodash';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import { SelectedAssetAmountInput } from '../containers/selected_asset_amount_input';
|
import { SelectedAssetAmountInput } from '../containers/selected_asset_amount_input';
|
||||||
import { ColorOption } from '../style/theme';
|
import { ColorOption } from '../style/theme';
|
||||||
|
import { AsyncProcessState } from '../types';
|
||||||
|
import { format } from '../util/format';
|
||||||
|
|
||||||
import { Container, Flex, Text } from './ui';
|
import { Container, Flex, Text } from './ui';
|
||||||
|
|
||||||
export interface InstantHeadingProps {}
|
export interface InstantHeadingProps {
|
||||||
|
selectedAssetAmount?: BigNumber;
|
||||||
|
totalEthBaseAmount?: BigNumber;
|
||||||
|
ethUsdPrice?: BigNumber;
|
||||||
|
quoteState: AsyncProcessState;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Placeholder = () => (
|
||||||
|
<Text fontWeight="bold" fontColor={ColorOption.white}>
|
||||||
|
—
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
const displaytotalEthBaseAmount = ({
|
||||||
|
selectedAssetAmount,
|
||||||
|
totalEthBaseAmount,
|
||||||
|
}: InstantHeadingProps): React.ReactNode => {
|
||||||
|
if (_.isUndefined(selectedAssetAmount)) {
|
||||||
|
return '0 ETH';
|
||||||
|
}
|
||||||
|
return format.ethBaseAmount(totalEthBaseAmount, 4, <Placeholder />);
|
||||||
|
};
|
||||||
|
|
||||||
|
const displayUsdAmount = ({
|
||||||
|
totalEthBaseAmount,
|
||||||
|
selectedAssetAmount,
|
||||||
|
ethUsdPrice,
|
||||||
|
}: InstantHeadingProps): React.ReactNode => {
|
||||||
|
if (_.isUndefined(selectedAssetAmount)) {
|
||||||
|
return '$0.00';
|
||||||
|
}
|
||||||
|
return format.ethBaseAmountInUsd(totalEthBaseAmount, ethUsdPrice, 2, <Placeholder />);
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadingOrAmount = (quoteState: AsyncProcessState, amount: React.ReactNode): React.ReactNode => {
|
||||||
|
if (quoteState === AsyncProcessState.PENDING) {
|
||||||
|
return (
|
||||||
|
<Text fontWeight="bold" fontColor={ColorOption.white}>
|
||||||
|
…loading
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const InstantHeading: React.StatelessComponent<InstantHeadingProps> = props => (
|
export const InstantHeading: React.StatelessComponent<InstantHeadingProps> = props => (
|
||||||
<Container backgroundColor={ColorOption.primaryColor} padding="20px" width="100%" borderRadius="3px 3px 0px 0px">
|
<Container backgroundColor={ColorOption.primaryColor} padding="20px" width="100%" borderRadius="3px 3px 0px 0px">
|
||||||
@ -22,22 +69,15 @@ export const InstantHeading: React.StatelessComponent<InstantHeadingProps> = pro
|
|||||||
</Text>
|
</Text>
|
||||||
</Container>
|
</Container>
|
||||||
<Flex direction="row" justify="space-between">
|
<Flex direction="row" justify="space-between">
|
||||||
<Container>
|
|
||||||
<SelectedAssetAmountInput fontSize="45px" />
|
<SelectedAssetAmountInput fontSize="45px" />
|
||||||
<Container display="inline-block" marginLeft="10px">
|
|
||||||
<Text fontSize="45px" fontColor={ColorOption.white} textTransform="uppercase">
|
|
||||||
rep
|
|
||||||
</Text>
|
|
||||||
</Container>
|
|
||||||
</Container>
|
|
||||||
<Flex direction="column" justify="space-between">
|
<Flex direction="column" justify="space-between">
|
||||||
<Container marginBottom="5px">
|
<Container marginBottom="5px">
|
||||||
<Text fontSize="16px" fontColor={ColorOption.white} fontWeight={500}>
|
<Text fontSize="16px" fontColor={ColorOption.white} fontWeight={500}>
|
||||||
0 ETH
|
{loadingOrAmount(props.quoteState, displaytotalEthBaseAmount(props))}
|
||||||
</Text>
|
</Text>
|
||||||
</Container>
|
</Container>
|
||||||
<Text fontSize="16px" fontColor={ColorOption.white} opacity={0.7}>
|
<Text fontSize="16px" fontColor={ColorOption.white} opacity={0.7}>
|
||||||
$0.00
|
{loadingOrAmount(props.quoteState, displayUsdAmount(props))}
|
||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -1,12 +1,27 @@
|
|||||||
|
import { BuyQuoteInfo } from '@0xproject/asset-buyer';
|
||||||
|
import { BigNumber } from '@0xproject/utils';
|
||||||
|
import * as _ from 'lodash';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import { oc } from 'ts-optchain';
|
||||||
|
|
||||||
import { ColorOption } from '../style/theme';
|
import { ColorOption } from '../style/theme';
|
||||||
|
import { format } from '../util/format';
|
||||||
|
|
||||||
import { Container, Flex, Text } from './ui';
|
import { Container, Flex, Text } from './ui';
|
||||||
|
|
||||||
export interface OrderDetailsProps {}
|
export interface OrderDetailsProps {
|
||||||
|
buyQuoteInfo?: BuyQuoteInfo;
|
||||||
|
ethUsdPrice?: BigNumber;
|
||||||
|
}
|
||||||
|
|
||||||
export const OrderDetails: React.StatelessComponent<OrderDetailsProps> = props => (
|
export class OrderDetails extends React.Component<OrderDetailsProps> {
|
||||||
|
public render(): React.ReactNode {
|
||||||
|
const { buyQuoteInfo, ethUsdPrice } = this.props;
|
||||||
|
const buyQuoteAccessor = oc(buyQuoteInfo);
|
||||||
|
const ethAssetPrice = buyQuoteAccessor.ethPerAssetPrice();
|
||||||
|
const ethTokenFee = buyQuoteAccessor.feeEthAmount();
|
||||||
|
const totalEthAmount = buyQuoteAccessor.totalEthAmount();
|
||||||
|
return (
|
||||||
<Container padding="20px" width="100%">
|
<Container padding="20px" width="100%">
|
||||||
<Container marginBottom="10px">
|
<Container marginBottom="10px">
|
||||||
<Text
|
<Text
|
||||||
@ -19,35 +34,57 @@ export const OrderDetails: React.StatelessComponent<OrderDetailsProps> = props =
|
|||||||
Order Details
|
Order Details
|
||||||
</Text>
|
</Text>
|
||||||
</Container>
|
</Container>
|
||||||
<OrderDetailsRow name="Token Price" primaryValue=".013 ETH" secondaryValue="$24.32" />
|
<EthAmountRow
|
||||||
<OrderDetailsRow name="Fee" primaryValue=".005 ETH" secondaryValue="$1.04" />
|
rowLabel="Token Price"
|
||||||
<OrderDetailsRow name="Total Cost" primaryValue="1.66 ETH" secondaryValue="$589.56" shouldEmphasize={true} />
|
ethAmount={ethAssetPrice}
|
||||||
|
ethUsdPrice={ethUsdPrice}
|
||||||
|
isEthAmountInBaseUnits={false}
|
||||||
|
/>
|
||||||
|
<EthAmountRow rowLabel="Fee" ethAmount={ethTokenFee} ethUsdPrice={ethUsdPrice} />
|
||||||
|
<EthAmountRow
|
||||||
|
rowLabel="Total Cost"
|
||||||
|
ethAmount={totalEthAmount}
|
||||||
|
ethUsdPrice={ethUsdPrice}
|
||||||
|
shouldEmphasize={true}
|
||||||
|
/>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
OrderDetails.displayName = 'OrderDetails';
|
export interface EthAmountRowProps {
|
||||||
|
rowLabel: string;
|
||||||
export interface OrderDetailsRowProps {
|
ethAmount?: BigNumber;
|
||||||
name: string;
|
isEthAmountInBaseUnits?: boolean;
|
||||||
primaryValue: string;
|
ethUsdPrice?: BigNumber;
|
||||||
secondaryValue: string;
|
|
||||||
shouldEmphasize?: boolean;
|
shouldEmphasize?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const OrderDetailsRow: React.StatelessComponent<OrderDetailsRowProps> = props => {
|
export const EthAmountRow: React.StatelessComponent<EthAmountRowProps> = ({
|
||||||
const fontWeight = props.shouldEmphasize ? 700 : 400;
|
rowLabel,
|
||||||
|
ethAmount,
|
||||||
|
isEthAmountInBaseUnits,
|
||||||
|
ethUsdPrice,
|
||||||
|
shouldEmphasize,
|
||||||
|
}) => {
|
||||||
|
const fontWeight = shouldEmphasize ? 700 : 400;
|
||||||
|
const usdFormatter = isEthAmountInBaseUnits ? format.ethBaseAmountInUsd : format.ethUnitAmountInUsd;
|
||||||
|
const ethFormatter = isEthAmountInBaseUnits ? format.ethBaseAmount : format.ethUnitAmount;
|
||||||
|
const usdPriceSection = _.isUndefined(ethUsdPrice) ? null : (
|
||||||
|
<Container marginRight="3px" display="inline-block">
|
||||||
|
<Text fontColor={ColorOption.lightGrey}>({usdFormatter(ethAmount, ethUsdPrice)})</Text>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<Container padding="10px 0px" borderTop="1px dashed" borderColor={ColorOption.feintGrey}>
|
<Container padding="10px 0px" borderTop="1px dashed" borderColor={ColorOption.feintGrey}>
|
||||||
<Flex justify="space-between">
|
<Flex justify="space-between">
|
||||||
<Text fontWeight={fontWeight} fontColor={ColorOption.grey}>
|
<Text fontWeight={fontWeight} fontColor={ColorOption.grey}>
|
||||||
{props.name}
|
{rowLabel}
|
||||||
</Text>
|
</Text>
|
||||||
<Container>
|
<Container>
|
||||||
<Container marginRight="3px" display="inline-block">
|
{usdPriceSection}
|
||||||
<Text fontColor={ColorOption.lightGrey}>({props.secondaryValue}) </Text>
|
|
||||||
</Container>
|
|
||||||
<Text fontWeight={fontWeight} fontColor={ColorOption.grey}>
|
<Text fontWeight={fontWeight} fontColor={ColorOption.grey}>
|
||||||
{props.primaryValue}
|
{ethFormatter(ethAmount)}
|
||||||
</Text>
|
</Text>
|
||||||
</Container>
|
</Container>
|
||||||
</Flex>
|
</Flex>
|
||||||
@ -55,8 +92,9 @@ export const OrderDetailsRow: React.StatelessComponent<OrderDetailsRowProps> = p
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
OrderDetailsRow.defaultProps = {
|
EthAmountRow.defaultProps = {
|
||||||
shouldEmphasize: false,
|
shouldEmphasize: false,
|
||||||
|
isEthAmountInBaseUnits: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
OrderDetailsRow.displayName = 'OrderDetailsRow';
|
EthAmountRow.displayName = 'EthAmountRow';
|
||||||
|
@ -2,7 +2,7 @@ import * as React from 'react';
|
|||||||
|
|
||||||
import { ColorOption } from '../style/theme';
|
import { ColorOption } from '../style/theme';
|
||||||
|
|
||||||
import { SlideUpAndDownAnimation } from './animations/slide_up_and_down_animation';
|
import { SlideDownAnimation, SlideUpAnimation } from './animations/slide_animations';
|
||||||
|
|
||||||
import { Container, Text } from './ui';
|
import { Container, Text } from './ui';
|
||||||
|
|
||||||
@ -20,8 +20,8 @@ export const Error: React.StatelessComponent<ErrorProps> = props => (
|
|||||||
borderRadius="6px"
|
borderRadius="6px"
|
||||||
marginBottom="10px"
|
marginBottom="10px"
|
||||||
>
|
>
|
||||||
<Container marginRight="5px" display="inline">
|
<Container marginRight="5px" display="inline" top="3px" position="relative">
|
||||||
{props.icon}
|
<Text fontSize="20px">{props.icon}</Text>
|
||||||
</Container>
|
</Container>
|
||||||
<Text fontWeight="500" fontColor={ColorOption.darkOrange}>
|
<Text fontWeight="500" fontColor={ColorOption.darkOrange}>
|
||||||
{props.message}
|
{props.message}
|
||||||
@ -29,8 +29,16 @@ export const Error: React.StatelessComponent<ErrorProps> = props => (
|
|||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const SlidingError: React.StatelessComponent<ErrorProps> = props => (
|
export type SlidingDirection = 'up' | 'down';
|
||||||
<SlideUpAndDownAnimation downY="120px" delayMs={5000}>
|
export interface SlidingErrorProps extends ErrorProps {
|
||||||
|
direction: SlidingDirection;
|
||||||
|
}
|
||||||
|
export const SlidingError: React.StatelessComponent<SlidingErrorProps> = props => {
|
||||||
|
const AnimationComponent = props.direction === 'up' ? SlideUpAnimation : SlideDownAnimation;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AnimationComponent downY="120px">
|
||||||
<Error icon={props.icon} message={props.message} />
|
<Error icon={props.icon} message={props.message} />
|
||||||
</SlideUpAndDownAnimation>
|
</AnimationComponent>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
|
|
||||||
|
import { asyncData } from '../redux/async_data';
|
||||||
import { store } from '../redux/store';
|
import { store } from '../redux/store';
|
||||||
import { fonts } from '../style/fonts';
|
import { fonts } from '../style/fonts';
|
||||||
import { theme, ThemeProvider } from '../style/theme';
|
import { theme, ThemeProvider } from '../style/theme';
|
||||||
@ -8,6 +9,8 @@ import { theme, ThemeProvider } from '../style/theme';
|
|||||||
import { ZeroExInstantContainer } from './zero_ex_instant_container';
|
import { ZeroExInstantContainer } from './zero_ex_instant_container';
|
||||||
|
|
||||||
fonts.include();
|
fonts.include();
|
||||||
|
// tslint:disable-next-line:no-floating-promises
|
||||||
|
asyncData.fetchAndDispatchToStore();
|
||||||
|
|
||||||
export interface ZeroExInstantProps {}
|
export interface ZeroExInstantProps {}
|
||||||
|
|
||||||
|
@ -1,16 +1,21 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { LatestBuyQuoteOrderDetails } from '../containers/latest_buy_quote_order_details';
|
||||||
|
import { LatestError } from '../containers/latest_error';
|
||||||
|
import { SelectedAssetBuyButton } from '../containers/selected_asset_buy_button';
|
||||||
|
import { SelectedAssetInstantHeading } from '../containers/selected_asset_instant_heading';
|
||||||
|
|
||||||
import { ColorOption } from '../style/theme';
|
import { ColorOption } from '../style/theme';
|
||||||
|
|
||||||
import { BuyButton } from './buy_button';
|
|
||||||
import { InstantHeading } from './instant_heading';
|
|
||||||
import { OrderDetails } from './order_details';
|
|
||||||
import { Container, Flex } from './ui';
|
import { Container, Flex } from './ui';
|
||||||
|
|
||||||
export interface ZeroExInstantContainerProps {}
|
export interface ZeroExInstantContainerProps {}
|
||||||
|
|
||||||
export const ZeroExInstantContainer: React.StatelessComponent<ZeroExInstantContainerProps> = props => (
|
export const ZeroExInstantContainer: React.StatelessComponent<ZeroExInstantContainerProps> = props => (
|
||||||
<Container width="350px">
|
<Container width="350px">
|
||||||
|
<Container zIndex={1} position="relative">
|
||||||
|
<LatestError />
|
||||||
|
</Container>
|
||||||
<Container
|
<Container
|
||||||
zIndex={2}
|
zIndex={2}
|
||||||
position="relative"
|
position="relative"
|
||||||
@ -19,9 +24,9 @@ export const ZeroExInstantContainer: React.StatelessComponent<ZeroExInstantConta
|
|||||||
hasBoxShadow={true}
|
hasBoxShadow={true}
|
||||||
>
|
>
|
||||||
<Flex direction="column" justify="flex-start">
|
<Flex direction="column" justify="flex-start">
|
||||||
<InstantHeading />
|
<SelectedAssetInstantHeading />
|
||||||
<OrderDetails />
|
<LatestBuyQuoteOrderDetails />
|
||||||
<BuyButton />
|
<SelectedAssetBuyButton />
|
||||||
</Flex>
|
</Flex>
|
||||||
</Container>
|
</Container>
|
||||||
</Container>
|
</Container>
|
||||||
|
6
packages/instant/src/constants.ts
Normal file
6
packages/instant/src/constants.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { BigNumber } from '@0xproject/utils';
|
||||||
|
export const BIG_NUMBER_ZERO = new BigNumber(0);
|
||||||
|
export const sraApiUrl = 'https://api.radarrelay.com/0x/v2/';
|
||||||
|
export const zrxAssetData = '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498';
|
||||||
|
export const zrxDecimals = 18;
|
||||||
|
export const ethDecimals = 18;
|
@ -0,0 +1,27 @@
|
|||||||
|
import { BuyQuoteInfo } from '@0xproject/asset-buyer';
|
||||||
|
import { BigNumber } from '@0xproject/utils';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { oc } from 'ts-optchain';
|
||||||
|
|
||||||
|
import { State } from '../redux/reducer';
|
||||||
|
|
||||||
|
import { OrderDetails } from '../components/order_details';
|
||||||
|
|
||||||
|
export interface LatestBuyQuoteOrderDetailsProps {}
|
||||||
|
|
||||||
|
interface ConnectedState {
|
||||||
|
buyQuoteInfo?: BuyQuoteInfo;
|
||||||
|
ethUsdPrice?: BigNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state: State, _ownProps: LatestBuyQuoteOrderDetailsProps): ConnectedState => ({
|
||||||
|
// use the worst case quote info
|
||||||
|
buyQuoteInfo: oc(state).latestBuyQuote.worstCaseQuoteInfo(),
|
||||||
|
ethUsdPrice: state.ethUsdPrice,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const LatestBuyQuoteOrderDetails: React.ComponentClass<LatestBuyQuoteOrderDetailsProps> = connect(
|
||||||
|
mapStateToProps,
|
||||||
|
)(OrderDetails);
|
35
packages/instant/src/containers/latest_error.tsx
Normal file
35
packages/instant/src/containers/latest_error.tsx
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { SlidingError } from '../components/sliding_error';
|
||||||
|
import { LatestErrorDisplay, State } from '../redux/reducer';
|
||||||
|
import { errorUtil } from '../util/error';
|
||||||
|
|
||||||
|
export interface LatestErrorComponentProps {
|
||||||
|
assetData?: string;
|
||||||
|
latestError?: any;
|
||||||
|
slidingDirection: 'down' | 'up';
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LatestErrorComponent: React.StatelessComponent<LatestErrorComponentProps> = props => {
|
||||||
|
if (!props.latestError) {
|
||||||
|
return <div />;
|
||||||
|
}
|
||||||
|
const { icon, message } = errorUtil.errorDescription(props.latestError, props.assetData);
|
||||||
|
return <SlidingError direction={props.slidingDirection} icon={icon} message={message} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface ConnectedState {
|
||||||
|
assetData?: string;
|
||||||
|
latestError?: any;
|
||||||
|
slidingDirection: 'down' | 'up';
|
||||||
|
}
|
||||||
|
export interface LatestErrorProps {}
|
||||||
|
const mapStateToProps = (state: State, _ownProps: LatestErrorProps): ConnectedState => ({
|
||||||
|
assetData: state.selectedAssetData,
|
||||||
|
latestError: state.latestError,
|
||||||
|
slidingDirection: state.latestErrorDisplay === LatestErrorDisplay.Present ? 'up' : 'down',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const LatestError = connect(mapStateToProps)(LatestErrorComponent);
|
@ -0,0 +1,89 @@
|
|||||||
|
import { BuyQuote } from '@0xproject/asset-buyer';
|
||||||
|
import { BigNumber } from '@0xproject/utils';
|
||||||
|
import { Web3Wrapper } from '@0xproject/web3-wrapper';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { Dispatch } from 'redux';
|
||||||
|
|
||||||
|
import { zrxDecimals } from '../constants';
|
||||||
|
import { Action, actions } from '../redux/actions';
|
||||||
|
import { State } from '../redux/reducer';
|
||||||
|
import { ColorOption } from '../style/theme';
|
||||||
|
import { AsyncProcessState } from '../types';
|
||||||
|
import { assetBuyer } from '../util/asset_buyer';
|
||||||
|
import { errorUtil } from '../util/error';
|
||||||
|
|
||||||
|
import { AssetAmountInput } from '../components/asset_amount_input';
|
||||||
|
|
||||||
|
export interface SelectedAssetAmountInputProps {
|
||||||
|
fontColor?: ColorOption;
|
||||||
|
fontSize?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ConnectedState {
|
||||||
|
value?: BigNumber;
|
||||||
|
assetData?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ConnectedDispatch {
|
||||||
|
onChange: (value?: BigNumber, assetData?: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state: State, _ownProps: SelectedAssetAmountInputProps): ConnectedState => ({
|
||||||
|
value: state.selectedAssetAmount,
|
||||||
|
assetData: state.selectedAssetData,
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateBuyQuoteAsync = async (
|
||||||
|
dispatch: Dispatch<Action>,
|
||||||
|
assetData: string,
|
||||||
|
assetAmount: BigNumber,
|
||||||
|
): Promise<void> => {
|
||||||
|
// get a new buy quote.
|
||||||
|
const baseUnitValue = Web3Wrapper.toBaseUnitAmount(assetAmount, zrxDecimals);
|
||||||
|
|
||||||
|
// mark quote as pending
|
||||||
|
dispatch(actions.updateBuyQuoteStatePending());
|
||||||
|
|
||||||
|
let newBuyQuote: BuyQuote | undefined;
|
||||||
|
try {
|
||||||
|
newBuyQuote = await assetBuyer.getBuyQuoteAsync(assetData, baseUnitValue);
|
||||||
|
} catch (error) {
|
||||||
|
dispatch(actions.updateBuyQuoteStateFailure());
|
||||||
|
errorUtil.errorFlasher.flashNewError(dispatch, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// We have a successful new buy quote
|
||||||
|
errorUtil.errorFlasher.clearError(dispatch);
|
||||||
|
// invalidate the last buy quote.
|
||||||
|
dispatch(actions.updateLatestBuyQuote(newBuyQuote));
|
||||||
|
};
|
||||||
|
|
||||||
|
const debouncedUpdateBuyQuoteAsync = _.debounce(updateBuyQuoteAsync, 200, { trailing: true });
|
||||||
|
|
||||||
|
const mapDispatchToProps = (
|
||||||
|
dispatch: Dispatch<Action>,
|
||||||
|
_ownProps: SelectedAssetAmountInputProps,
|
||||||
|
): ConnectedDispatch => ({
|
||||||
|
onChange: (value, assetData) => {
|
||||||
|
// Update the input
|
||||||
|
dispatch(actions.updateSelectedAssetAmount(value));
|
||||||
|
// invalidate the last buy quote.
|
||||||
|
dispatch(actions.updateLatestBuyQuote(undefined));
|
||||||
|
// reset our buy state
|
||||||
|
dispatch(actions.updatebuyOrderState(AsyncProcessState.NONE));
|
||||||
|
|
||||||
|
if (!_.isUndefined(value) && !_.isUndefined(assetData)) {
|
||||||
|
// even if it's debounced, give them the illusion it's loading
|
||||||
|
dispatch(actions.updateBuyQuoteStatePending());
|
||||||
|
// tslint:disable-next-line:no-floating-promises
|
||||||
|
debouncedUpdateBuyQuoteAsync(dispatch, assetData, value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SelectedAssetAmountInput: React.ComponentClass<SelectedAssetAmountInputProps> = connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps,
|
||||||
|
)(AssetAmountInput);
|
@ -1,36 +0,0 @@
|
|||||||
import { BigNumber } from '@0xproject/utils';
|
|
||||||
import * as React from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { Dispatch } from 'redux';
|
|
||||||
|
|
||||||
import { State } from '../redux/reducer';
|
|
||||||
import { ColorOption } from '../style/theme';
|
|
||||||
import { Action, ActionTypes } from '../types';
|
|
||||||
|
|
||||||
import { AmountInput } from '../components/amount_input';
|
|
||||||
|
|
||||||
export interface SelectedAssetAmountInputProps {
|
|
||||||
fontColor?: ColorOption;
|
|
||||||
fontSize?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ConnectedState {
|
|
||||||
value?: BigNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ConnectedDispatch {
|
|
||||||
onChange?: (value?: BigNumber) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapStateToProps = (state: State, _ownProps: SelectedAssetAmountInputProps): ConnectedState => ({
|
|
||||||
value: state.selectedAssetAmount,
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch: Dispatch<Action>): ConnectedDispatch => ({
|
|
||||||
onChange: value => dispatch({ type: ActionTypes.UPDATE_SELECTED_ASSET_AMOUNT, data: value }),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const SelectedAssetAmountInput: React.ComponentClass<SelectedAssetAmountInputProps> = connect(
|
|
||||||
mapStateToProps,
|
|
||||||
mapDispatchToProps,
|
|
||||||
)(AmountInput);
|
|
55
packages/instant/src/containers/selected_asset_buy_button.ts
Normal file
55
packages/instant/src/containers/selected_asset_buy_button.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import { BuyQuote } from '@0xproject/asset-buyer';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { Dispatch } from 'redux';
|
||||||
|
|
||||||
|
import { Action, actions } from '../redux/actions';
|
||||||
|
import { State } from '../redux/reducer';
|
||||||
|
import { AsyncProcessState } from '../types';
|
||||||
|
|
||||||
|
import { BuyButton } from '../components/buy_button';
|
||||||
|
|
||||||
|
export interface SelectedAssetBuyButtonProps {}
|
||||||
|
|
||||||
|
interface ConnectedState {
|
||||||
|
text: string;
|
||||||
|
buyQuote?: BuyQuote;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ConnectedDispatch {
|
||||||
|
onClick: (buyQuote: BuyQuote) => void;
|
||||||
|
onBuySuccess: (buyQuote: BuyQuote) => void;
|
||||||
|
onBuyFailure: (buyQuote: BuyQuote) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const textForState = (state: AsyncProcessState): string => {
|
||||||
|
switch (state) {
|
||||||
|
case AsyncProcessState.NONE:
|
||||||
|
return 'Buy';
|
||||||
|
case AsyncProcessState.PENDING:
|
||||||
|
return '...Loading';
|
||||||
|
case AsyncProcessState.SUCCESS:
|
||||||
|
return 'Success!';
|
||||||
|
case AsyncProcessState.FAILURE:
|
||||||
|
return 'Failed';
|
||||||
|
default:
|
||||||
|
return 'Buy';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapStateToProps = (state: State, _ownProps: SelectedAssetBuyButtonProps): ConnectedState => ({
|
||||||
|
text: textForState(state.buyOrderState),
|
||||||
|
buyQuote: state.latestBuyQuote,
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch: Dispatch<Action>, ownProps: SelectedAssetBuyButtonProps): ConnectedDispatch => ({
|
||||||
|
onClick: buyQuote => dispatch(actions.updatebuyOrderState(AsyncProcessState.PENDING)),
|
||||||
|
onBuySuccess: buyQuote => dispatch(actions.updatebuyOrderState(AsyncProcessState.SUCCESS)),
|
||||||
|
onBuyFailure: buyQuote => dispatch(actions.updatebuyOrderState(AsyncProcessState.FAILURE)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SelectedAssetBuyButton: React.ComponentClass<SelectedAssetBuyButtonProps> = connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps,
|
||||||
|
)(BuyButton);
|
@ -0,0 +1,30 @@
|
|||||||
|
import { BigNumber } from '@0xproject/utils';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { oc } from 'ts-optchain';
|
||||||
|
|
||||||
|
import { State } from '../redux/reducer';
|
||||||
|
import { AsyncProcessState } from '../types';
|
||||||
|
|
||||||
|
import { InstantHeading } from '../components/instant_heading';
|
||||||
|
|
||||||
|
export interface InstantHeadingProps {}
|
||||||
|
|
||||||
|
interface ConnectedState {
|
||||||
|
selectedAssetAmount?: BigNumber;
|
||||||
|
totalEthBaseAmount?: BigNumber;
|
||||||
|
ethUsdPrice?: BigNumber;
|
||||||
|
quoteState: AsyncProcessState;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state: State, _ownProps: InstantHeadingProps): ConnectedState => ({
|
||||||
|
selectedAssetAmount: state.selectedAssetAmount,
|
||||||
|
totalEthBaseAmount: oc(state).latestBuyQuote.worstCaseQuoteInfo.totalEthAmount(),
|
||||||
|
ethUsdPrice: state.ethUsdPrice,
|
||||||
|
quoteState: state.quoteState,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SelectedAssetInstantHeading: React.ComponentClass<InstantHeadingProps> = connect(mapStateToProps)(
|
||||||
|
InstantHeading,
|
||||||
|
);
|
15
packages/instant/src/data/asset_meta_data.ts
Normal file
15
packages/instant/src/data/asset_meta_data.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { AssetProxyId, ObjectMap } from '@0xproject/types';
|
||||||
|
|
||||||
|
import { zrxAssetData } from '../constants';
|
||||||
|
import { AssetMetaData } from '../types';
|
||||||
|
|
||||||
|
// Map from assetData string to AssetMetaData object
|
||||||
|
// TODO: import this from somewhere else.
|
||||||
|
export const assetMetaData: ObjectMap<AssetMetaData> = {
|
||||||
|
[zrxAssetData]: {
|
||||||
|
assetProxyId: AssetProxyId.ERC20,
|
||||||
|
decimals: 18,
|
||||||
|
primaryColor: 'rgb(54, 50, 60)',
|
||||||
|
symbol: 'zrx',
|
||||||
|
},
|
||||||
|
};
|
46
packages/instant/src/redux/actions.ts
Normal file
46
packages/instant/src/redux/actions.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { BuyQuote } from '@0xproject/asset-buyer';
|
||||||
|
import { BigNumber } from '@0xproject/utils';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
import { ActionsUnion, AsyncProcessState } from '../types';
|
||||||
|
|
||||||
|
export interface PlainAction<T extends string> {
|
||||||
|
type: T;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ActionWithPayload<T extends string, P> extends PlainAction<T> {
|
||||||
|
data: P;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Action = ActionsUnion<typeof actions>;
|
||||||
|
|
||||||
|
function createAction<T extends string>(type: T): PlainAction<T>;
|
||||||
|
function createAction<T extends string, P>(type: T, data: P): ActionWithPayload<T, P>;
|
||||||
|
function createAction<T extends string, P>(type: T, data?: P): PlainAction<T> | ActionWithPayload<T, P> {
|
||||||
|
return _.isUndefined(data) ? { type } : { type, data };
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ActionTypes {
|
||||||
|
UPDATE_ETH_USD_PRICE = 'UPDATE_ETH_USD_PRICE',
|
||||||
|
UPDATE_SELECTED_ASSET_AMOUNT = 'UPDATE_SELECTED_ASSET_AMOUNT',
|
||||||
|
UPDATE_SELECTED_ASSET_BUY_STATE = 'UPDATE_SELECTED_ASSET_BUY_STATE',
|
||||||
|
UPDATE_LATEST_BUY_QUOTE = 'UPDATE_LATEST_BUY_QUOTE',
|
||||||
|
UPDATE_BUY_QUOTE_STATE_PENDING = 'UPDATE_BUY_QUOTE_STATE_PENDING',
|
||||||
|
UPDATE_BUY_QUOTE_STATE_FAILURE = 'UPDATE_BUY_QUOTE_STATE_FAILURE',
|
||||||
|
SET_ERROR = 'SET_ERROR',
|
||||||
|
HIDE_ERROR = 'HIDE_ERROR',
|
||||||
|
CLEAR_ERROR = 'CLEAR_ERROR',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const actions = {
|
||||||
|
updateEthUsdPrice: (price?: BigNumber) => createAction(ActionTypes.UPDATE_ETH_USD_PRICE, price),
|
||||||
|
updateSelectedAssetAmount: (amount?: BigNumber) => createAction(ActionTypes.UPDATE_SELECTED_ASSET_AMOUNT, amount),
|
||||||
|
updatebuyOrderState: (buyState: AsyncProcessState) =>
|
||||||
|
createAction(ActionTypes.UPDATE_SELECTED_ASSET_BUY_STATE, buyState),
|
||||||
|
updateLatestBuyQuote: (buyQuote?: BuyQuote) => createAction(ActionTypes.UPDATE_LATEST_BUY_QUOTE, buyQuote),
|
||||||
|
updateBuyQuoteStatePending: () => createAction(ActionTypes.UPDATE_BUY_QUOTE_STATE_PENDING),
|
||||||
|
updateBuyQuoteStateFailure: () => createAction(ActionTypes.UPDATE_BUY_QUOTE_STATE_FAILURE),
|
||||||
|
setError: (error?: any) => createAction(ActionTypes.SET_ERROR, error),
|
||||||
|
hideError: () => createAction(ActionTypes.HIDE_ERROR),
|
||||||
|
clearError: () => createAction(ActionTypes.CLEAR_ERROR),
|
||||||
|
};
|
22
packages/instant/src/redux/async_data.ts
Normal file
22
packages/instant/src/redux/async_data.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { BIG_NUMBER_ZERO } from '../constants';
|
||||||
|
import { coinbaseApi } from '../util/coinbase_api';
|
||||||
|
|
||||||
|
import { ActionTypes } from './actions';
|
||||||
|
|
||||||
|
import { store } from './store';
|
||||||
|
|
||||||
|
export const asyncData = {
|
||||||
|
fetchAndDispatchToStore: async () => {
|
||||||
|
let ethUsdPrice = BIG_NUMBER_ZERO;
|
||||||
|
try {
|
||||||
|
ethUsdPrice = await coinbaseApi.getEthUsdPrice();
|
||||||
|
} catch (e) {
|
||||||
|
// ignore
|
||||||
|
} finally {
|
||||||
|
store.dispatch({
|
||||||
|
type: ActionTypes.UPDATE_ETH_USD_PRICE,
|
||||||
|
data: ethUsdPrice,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
@ -1,16 +1,37 @@
|
|||||||
|
import { BuyQuote } from '@0xproject/asset-buyer';
|
||||||
import { BigNumber } from '@0xproject/utils';
|
import { BigNumber } from '@0xproject/utils';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { Action, ActionTypes } from '../types';
|
import { zrxAssetData } from '../constants';
|
||||||
|
import { AsyncProcessState } from '../types';
|
||||||
|
|
||||||
|
import { Action, ActionTypes } from './actions';
|
||||||
|
|
||||||
|
export enum LatestErrorDisplay {
|
||||||
|
Present,
|
||||||
|
Hidden,
|
||||||
|
}
|
||||||
export interface State {
|
export interface State {
|
||||||
ethUsdPrice?: string;
|
selectedAssetData?: string;
|
||||||
selectedAssetAmount?: BigNumber;
|
selectedAssetAmount?: BigNumber;
|
||||||
|
buyOrderState: AsyncProcessState;
|
||||||
|
ethUsdPrice?: BigNumber;
|
||||||
|
latestBuyQuote?: BuyQuote;
|
||||||
|
quoteState: AsyncProcessState;
|
||||||
|
latestError?: any;
|
||||||
|
latestErrorDisplay: LatestErrorDisplay;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const INITIAL_STATE: State = {
|
export const INITIAL_STATE: State = {
|
||||||
ethUsdPrice: undefined,
|
// TODO: Remove hardcoded zrxAssetData
|
||||||
|
selectedAssetData: zrxAssetData,
|
||||||
selectedAssetAmount: undefined,
|
selectedAssetAmount: undefined,
|
||||||
|
buyOrderState: AsyncProcessState.NONE,
|
||||||
|
ethUsdPrice: undefined,
|
||||||
|
latestBuyQuote: undefined,
|
||||||
|
latestError: undefined,
|
||||||
|
latestErrorDisplay: LatestErrorDisplay.Hidden,
|
||||||
|
quoteState: AsyncProcessState.NONE,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const reducer = (state: State = INITIAL_STATE, action: Action): State => {
|
export const reducer = (state: State = INITIAL_STATE, action: Action): State => {
|
||||||
@ -25,6 +46,46 @@ export const reducer = (state: State = INITIAL_STATE, action: Action): State =>
|
|||||||
...state,
|
...state,
|
||||||
selectedAssetAmount: action.data,
|
selectedAssetAmount: action.data,
|
||||||
};
|
};
|
||||||
|
case ActionTypes.UPDATE_LATEST_BUY_QUOTE:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
latestBuyQuote: action.data,
|
||||||
|
quoteState: AsyncProcessState.SUCCESS,
|
||||||
|
};
|
||||||
|
case ActionTypes.UPDATE_BUY_QUOTE_STATE_PENDING:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
latestBuyQuote: undefined,
|
||||||
|
quoteState: AsyncProcessState.PENDING,
|
||||||
|
};
|
||||||
|
case ActionTypes.UPDATE_BUY_QUOTE_STATE_FAILURE:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
latestBuyQuote: undefined,
|
||||||
|
quoteState: AsyncProcessState.FAILURE,
|
||||||
|
};
|
||||||
|
case ActionTypes.UPDATE_SELECTED_ASSET_BUY_STATE:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
buyOrderState: action.data,
|
||||||
|
};
|
||||||
|
case ActionTypes.SET_ERROR:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
latestError: action.data,
|
||||||
|
latestErrorDisplay: LatestErrorDisplay.Present,
|
||||||
|
};
|
||||||
|
case ActionTypes.HIDE_ERROR:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
latestErrorDisplay: LatestErrorDisplay.Hidden,
|
||||||
|
};
|
||||||
|
case ActionTypes.CLEAR_ERROR:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
latestError: undefined,
|
||||||
|
latestErrorDisplay: LatestErrorDisplay.Hidden,
|
||||||
|
};
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import { createStore, Store as ReduxStore } from 'redux';
|
import { createStore, Store as ReduxStore } from 'redux';
|
||||||
|
import { devToolsEnhancer } from 'redux-devtools-extension/developmentOnly';
|
||||||
|
|
||||||
import { reducer, State } from './reducer';
|
import { reducer, State } from './reducer';
|
||||||
|
|
||||||
export const store: ReduxStore<State> = createStore(reducer);
|
export const store: ReduxStore<State> = createStore(reducer, devToolsEnhancer({}));
|
||||||
|
@ -1,9 +1,33 @@
|
|||||||
export enum ActionTypes {
|
import { AssetProxyId, ObjectMap } from '@0xproject/types';
|
||||||
UPDATE_ETH_USD_PRICE,
|
|
||||||
UPDATE_SELECTED_ASSET_AMOUNT,
|
// Reusable
|
||||||
|
export enum AsyncProcessState {
|
||||||
|
NONE = 'None',
|
||||||
|
PENDING = 'Pending',
|
||||||
|
SUCCESS = 'Success',
|
||||||
|
FAILURE = 'Failure',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Action {
|
export type FunctionType = (...args: any[]) => any;
|
||||||
type: ActionTypes;
|
export type ActionCreatorsMapObject = ObjectMap<FunctionType>;
|
||||||
data?: any;
|
export type ActionsUnion<A extends ActionCreatorsMapObject> = ReturnType<A[keyof A]>;
|
||||||
|
|
||||||
|
export interface ERC20AssetMetaData {
|
||||||
|
assetProxyId: AssetProxyId.ERC20;
|
||||||
|
decimals: number;
|
||||||
|
primaryColor?: string;
|
||||||
|
symbol: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ERC721AssetMetaData {
|
||||||
|
assetProxyId: AssetProxyId.ERC721;
|
||||||
|
name: string;
|
||||||
|
primaryColor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AssetMetaData = ERC20AssetMetaData | ERC721AssetMetaData;
|
||||||
|
|
||||||
|
export enum Network {
|
||||||
|
Kovan = 42,
|
||||||
|
Mainnet = 1,
|
||||||
}
|
}
|
||||||
|
9
packages/instant/src/util/asset_buyer.ts
Normal file
9
packages/instant/src/util/asset_buyer.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { AssetBuyer } from '@0xproject/asset-buyer';
|
||||||
|
|
||||||
|
import { sraApiUrl } from '../constants';
|
||||||
|
|
||||||
|
import { getProvider } from './provider';
|
||||||
|
|
||||||
|
const provider = getProvider();
|
||||||
|
|
||||||
|
export const assetBuyer = AssetBuyer.getAssetBuyerForStandardRelayerAPIUrl(provider, sraApiUrl);
|
21
packages/instant/src/util/asset_data.ts
Normal file
21
packages/instant/src/util/asset_data.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
import { AssetProxyId } from '@0xproject/types';
|
||||||
|
|
||||||
|
import { assetMetaData } from '../data/asset_meta_data';
|
||||||
|
|
||||||
|
export const assetDataUtil = {
|
||||||
|
bestNameForAsset: (assetData: string | undefined, defaultString: string) => {
|
||||||
|
if (_.isUndefined(assetData)) {
|
||||||
|
return defaultString;
|
||||||
|
}
|
||||||
|
const metaData = assetMetaData[assetData];
|
||||||
|
if (_.isUndefined(metaData)) {
|
||||||
|
return defaultString;
|
||||||
|
}
|
||||||
|
if (metaData.assetProxyId === AssetProxyId.ERC20) {
|
||||||
|
return metaData.symbol.toUpperCase();
|
||||||
|
}
|
||||||
|
return defaultString;
|
||||||
|
},
|
||||||
|
};
|
10
packages/instant/src/util/coinbase_api.ts
Normal file
10
packages/instant/src/util/coinbase_api.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { BigNumber } from '@0xproject/utils';
|
||||||
|
|
||||||
|
const baseEndpoint = 'https://api.coinbase.com/v2';
|
||||||
|
export const coinbaseApi = {
|
||||||
|
getEthUsdPrice: async (): Promise<BigNumber> => {
|
||||||
|
const res = await fetch(`${baseEndpoint}/prices/ETH-USD/buy`);
|
||||||
|
const resJson = await res.json();
|
||||||
|
return new BigNumber(resJson.data.amount);
|
||||||
|
},
|
||||||
|
};
|
62
packages/instant/src/util/error.ts
Normal file
62
packages/instant/src/util/error.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { AssetBuyerError } from '@0xproject/asset-buyer';
|
||||||
|
import { Dispatch } from 'redux';
|
||||||
|
|
||||||
|
import { Action, actions } from '../redux/actions';
|
||||||
|
import { assetDataUtil } from '../util/asset_data';
|
||||||
|
|
||||||
|
class ErrorFlasher {
|
||||||
|
private _timeoutId?: number;
|
||||||
|
public flashNewError(dispatch: Dispatch<Action>, error: any, delayMs: number = 7000): void {
|
||||||
|
this._clearTimeout();
|
||||||
|
|
||||||
|
// dispatch new message
|
||||||
|
dispatch(actions.setError(error));
|
||||||
|
|
||||||
|
this._timeoutId = window.setTimeout(() => {
|
||||||
|
dispatch(actions.hideError());
|
||||||
|
}, delayMs);
|
||||||
|
}
|
||||||
|
public clearError(dispatch: Dispatch<Action>): void {
|
||||||
|
this._clearTimeout();
|
||||||
|
dispatch(actions.hideError());
|
||||||
|
}
|
||||||
|
private _clearTimeout(): void {
|
||||||
|
if (this._timeoutId) {
|
||||||
|
window.clearTimeout(this._timeoutId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const humanReadableMessageForError = (error: Error, assetData?: string): string | undefined => {
|
||||||
|
const hasInsufficientLiquidity =
|
||||||
|
error.message === AssetBuyerError.InsufficientAssetLiquidity ||
|
||||||
|
error.message === AssetBuyerError.InsufficientZrxLiquidity;
|
||||||
|
if (hasInsufficientLiquidity) {
|
||||||
|
const assetName = assetDataUtil.bestNameForAsset(assetData, 'of this asset');
|
||||||
|
return `Not enough ${assetName} available`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
error.message === AssetBuyerError.StandardRelayerApiError ||
|
||||||
|
error.message.startsWith(AssetBuyerError.AssetUnavailable)
|
||||||
|
) {
|
||||||
|
const assetName = assetDataUtil.bestNameForAsset(assetData, 'This asset');
|
||||||
|
return `${assetName} is currently unavailable`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const errorUtil = {
|
||||||
|
errorFlasher: new ErrorFlasher(),
|
||||||
|
errorDescription: (error?: any, assetData?: string): { icon: string; message: string } => {
|
||||||
|
let bestMessage: string | undefined;
|
||||||
|
if (error instanceof Error) {
|
||||||
|
bestMessage = humanReadableMessageForError(error, assetData);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
icon: '😢',
|
||||||
|
message: bestMessage || 'Something went wrong...',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
53
packages/instant/src/util/format.ts
Normal file
53
packages/instant/src/util/format.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { BigNumber } from '@0xproject/utils';
|
||||||
|
import { Web3Wrapper } from '@0xproject/web3-wrapper';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
import { ethDecimals } from '../constants';
|
||||||
|
|
||||||
|
export const format = {
|
||||||
|
ethBaseAmount: (
|
||||||
|
ethBaseAmount?: BigNumber,
|
||||||
|
decimalPlaces: number = 4,
|
||||||
|
defaultText: React.ReactNode = '0 ETH',
|
||||||
|
): React.ReactNode => {
|
||||||
|
if (_.isUndefined(ethBaseAmount)) {
|
||||||
|
return defaultText;
|
||||||
|
}
|
||||||
|
const ethUnitAmount = Web3Wrapper.toUnitAmount(ethBaseAmount, ethDecimals);
|
||||||
|
return format.ethUnitAmount(ethUnitAmount, decimalPlaces);
|
||||||
|
},
|
||||||
|
ethUnitAmount: (
|
||||||
|
ethUnitAmount?: BigNumber,
|
||||||
|
decimalPlaces: number = 4,
|
||||||
|
defaultText: React.ReactNode = '0 ETH',
|
||||||
|
): React.ReactNode => {
|
||||||
|
if (_.isUndefined(ethUnitAmount)) {
|
||||||
|
return defaultText;
|
||||||
|
}
|
||||||
|
const roundedAmount = ethUnitAmount.round(decimalPlaces);
|
||||||
|
return `${roundedAmount} ETH`;
|
||||||
|
},
|
||||||
|
ethBaseAmountInUsd: (
|
||||||
|
ethBaseAmount?: BigNumber,
|
||||||
|
ethUsdPrice?: BigNumber,
|
||||||
|
decimalPlaces: number = 2,
|
||||||
|
defaultText: React.ReactNode = '$0.00',
|
||||||
|
): React.ReactNode => {
|
||||||
|
if (_.isUndefined(ethBaseAmount) || _.isUndefined(ethUsdPrice)) {
|
||||||
|
return defaultText;
|
||||||
|
}
|
||||||
|
const ethUnitAmount = Web3Wrapper.toUnitAmount(ethBaseAmount, ethDecimals);
|
||||||
|
return format.ethUnitAmountInUsd(ethUnitAmount, ethUsdPrice, decimalPlaces);
|
||||||
|
},
|
||||||
|
ethUnitAmountInUsd: (
|
||||||
|
ethUnitAmount?: BigNumber,
|
||||||
|
ethUsdPrice?: BigNumber,
|
||||||
|
decimalPlaces: number = 2,
|
||||||
|
defaultText: React.ReactNode = '$0.00',
|
||||||
|
): React.ReactNode => {
|
||||||
|
if (_.isUndefined(ethUnitAmount) || _.isUndefined(ethUsdPrice)) {
|
||||||
|
return defaultText;
|
||||||
|
}
|
||||||
|
return `$${ethUnitAmount.mul(ethUsdPrice).toFixed(decimalPlaces)}`;
|
||||||
|
},
|
||||||
|
};
|
12
packages/instant/src/util/provider.ts
Normal file
12
packages/instant/src/util/provider.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { Provider } from 'ethereum-types';
|
||||||
|
|
||||||
|
export const getProvider = (): Provider => {
|
||||||
|
const injectedWeb3 = (window as any).web3 || undefined;
|
||||||
|
try {
|
||||||
|
// Use MetaMask/Mist provider
|
||||||
|
return injectedWeb3.currentProvider;
|
||||||
|
} catch (err) {
|
||||||
|
// Throws when user doesn't have MetaMask/Mist running
|
||||||
|
throw new Error(`No injected web3 found: ${err}`);
|
||||||
|
}
|
||||||
|
};
|
5
packages/instant/src/util/util.ts
Normal file
5
packages/instant/src/util/util.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
export const util = {
|
||||||
|
boundNoop: _.noop.bind(_),
|
||||||
|
};
|
5
packages/instant/src/util/web3_wrapper.ts
Normal file
5
packages/instant/src/util/web3_wrapper.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { Web3Wrapper } from '@0xproject/web3-wrapper';
|
||||||
|
|
||||||
|
import { getProvider } from './provider';
|
||||||
|
|
||||||
|
export const web3Wrapper = new Web3Wrapper(getProvider());
|
@ -4,10 +4,12 @@ import * as React from 'react';
|
|||||||
|
|
||||||
configure({ adapter: new Adapter() });
|
configure({ adapter: new Adapter() });
|
||||||
|
|
||||||
import { ZeroExInstant } from '../../src';
|
// TODO: Write non-trivial tests.
|
||||||
|
// At time of writing we cannot render ZeroExInstant
|
||||||
describe('<ZeroExInstant />', () => {
|
// because we are looking for a provider on window.
|
||||||
it('shallow renders without crashing', () => {
|
// But in the future it will be dependency injected.
|
||||||
shallow(<ZeroExInstant />);
|
describe('<Test />', () => {
|
||||||
|
it('runs a test', () => {
|
||||||
|
shallow(<div />);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
17
packages/instant/test/util/asset_data.test.ts
Normal file
17
packages/instant/test/util/asset_data.test.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { assetDataUtil } from '../../src/util/asset_data';
|
||||||
|
|
||||||
|
const ZRX_ASSET_DATA = '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498';
|
||||||
|
|
||||||
|
describe('assetDataUtil', () => {
|
||||||
|
describe('bestNameForAsset', () => {
|
||||||
|
it('should return default string if assetData is undefined', () => {
|
||||||
|
expect(assetDataUtil.bestNameForAsset(undefined, 'xyz')).toEqual('xyz');
|
||||||
|
});
|
||||||
|
it('should return default string if assetData isnt found', () => {
|
||||||
|
expect(assetDataUtil.bestNameForAsset('fake', 'mah default')).toEqual('mah default');
|
||||||
|
});
|
||||||
|
it('should return ZRX for ZRX assetData', () => {
|
||||||
|
expect(assetDataUtil.bestNameForAsset(ZRX_ASSET_DATA, 'mah default')).toEqual('ZRX');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
48
packages/instant/test/util/error.test.ts
Normal file
48
packages/instant/test/util/error.test.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { AssetBuyerError } from '@0xproject/asset-buyer';
|
||||||
|
|
||||||
|
import { errorUtil } from '../../src/util/error';
|
||||||
|
|
||||||
|
const ZRX_ASSET_DATA = '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498';
|
||||||
|
|
||||||
|
describe('errorUtil', () => {
|
||||||
|
describe('errorFlasher', () => {
|
||||||
|
it('should return error and asset name for InsufficientAssetLiquidity', () => {
|
||||||
|
const insufficientAssetError = new Error(AssetBuyerError.InsufficientAssetLiquidity);
|
||||||
|
expect(errorUtil.errorDescription(insufficientAssetError, ZRX_ASSET_DATA).message).toEqual(
|
||||||
|
'Not enough ZRX available',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('should return error default name for InsufficientAssetLiquidity', () => {
|
||||||
|
const insufficientZrxError = new Error(AssetBuyerError.InsufficientZrxLiquidity);
|
||||||
|
expect(errorUtil.errorDescription(insufficientZrxError).message).toEqual(
|
||||||
|
'Not enough of this asset available',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('should return asset name for InsufficientAssetLiquidity', () => {
|
||||||
|
const insufficientZrxError = new Error(AssetBuyerError.InsufficientZrxLiquidity);
|
||||||
|
expect(errorUtil.errorDescription(insufficientZrxError, ZRX_ASSET_DATA).message).toEqual(
|
||||||
|
'Not enough ZRX available',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('should return unavailable error and asset name for StandardRelayerApiError', () => {
|
||||||
|
const standardRelayerError = new Error(AssetBuyerError.StandardRelayerApiError);
|
||||||
|
expect(errorUtil.errorDescription(standardRelayerError, ZRX_ASSET_DATA).message).toEqual(
|
||||||
|
'ZRX is currently unavailable',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('should return error for AssetUnavailable error', () => {
|
||||||
|
const assetUnavailableError = new Error(
|
||||||
|
`${AssetBuyerError.AssetUnavailable}: For assetData ${ZRX_ASSET_DATA}`,
|
||||||
|
);
|
||||||
|
expect(errorUtil.errorDescription(assetUnavailableError, ZRX_ASSET_DATA).message).toEqual(
|
||||||
|
'ZRX is currently unavailable',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('should return default for AssetUnavailable error', () => {
|
||||||
|
const assetUnavailableError = new Error(`${AssetBuyerError.AssetUnavailable}: For assetData xyz`);
|
||||||
|
expect(errorUtil.errorDescription(assetUnavailableError, 'xyz').message).toEqual(
|
||||||
|
'This asset is currently unavailable',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
97
packages/instant/test/util/format.test.ts
Normal file
97
packages/instant/test/util/format.test.ts
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import { BigNumber } from '@0xproject/utils';
|
||||||
|
import { Web3Wrapper } from '@0xproject/web3-wrapper';
|
||||||
|
|
||||||
|
import { ethDecimals } from '../../src/constants';
|
||||||
|
import { format } from '../../src/util/format';
|
||||||
|
|
||||||
|
const BIG_NUMBER_ONE = new BigNumber(1);
|
||||||
|
const BIG_NUMBER_DECIMAL = new BigNumber(0.432414);
|
||||||
|
const BIG_NUMBER_IRRATIONAL = new BigNumber(5.3014059295032);
|
||||||
|
const ONE_ETH_IN_BASE_UNITS = Web3Wrapper.toBaseUnitAmount(BIG_NUMBER_ONE, ethDecimals);
|
||||||
|
const DECIMAL_ETH_IN_BASE_UNITS = Web3Wrapper.toBaseUnitAmount(BIG_NUMBER_DECIMAL, ethDecimals);
|
||||||
|
const IRRATIONAL_ETH_IN_BASE_UNITS = Web3Wrapper.toBaseUnitAmount(BIG_NUMBER_IRRATIONAL, ethDecimals);
|
||||||
|
const BIG_NUMBER_FAKE_ETH_USD_PRICE = new BigNumber(2.534);
|
||||||
|
|
||||||
|
describe('format', () => {
|
||||||
|
describe('ethBaseAmount', () => {
|
||||||
|
it('converts 1 ETH in base units to the string `1 ETH`', () => {
|
||||||
|
expect(format.ethBaseAmount(ONE_ETH_IN_BASE_UNITS)).toBe('1 ETH');
|
||||||
|
});
|
||||||
|
it('converts .432414 ETH in base units to the string `.4324 ETH`', () => {
|
||||||
|
expect(format.ethBaseAmount(DECIMAL_ETH_IN_BASE_UNITS)).toBe('0.4324 ETH');
|
||||||
|
});
|
||||||
|
it('converts 5.3014059295032 ETH in base units to the string `5.3014 ETH`', () => {
|
||||||
|
expect(format.ethBaseAmount(IRRATIONAL_ETH_IN_BASE_UNITS)).toBe('5.3014 ETH');
|
||||||
|
});
|
||||||
|
it('returns defaultText param when ethBaseAmount is not defined', () => {
|
||||||
|
const defaultText = 'defaultText';
|
||||||
|
expect(format.ethBaseAmount(undefined, 4, defaultText)).toBe(defaultText);
|
||||||
|
});
|
||||||
|
it('it allows for configurable decimal places', () => {
|
||||||
|
expect(format.ethBaseAmount(DECIMAL_ETH_IN_BASE_UNITS, 2)).toBe('0.43 ETH');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('ethUnitAmount', () => {
|
||||||
|
it('converts BigNumber(1) to the string `1 ETH`', () => {
|
||||||
|
expect(format.ethUnitAmount(BIG_NUMBER_ONE)).toBe('1 ETH');
|
||||||
|
});
|
||||||
|
it('converts BigNumer(.432414) to the string `.4324 ETH`', () => {
|
||||||
|
expect(format.ethUnitAmount(BIG_NUMBER_DECIMAL)).toBe('0.4324 ETH');
|
||||||
|
});
|
||||||
|
it('converts BigNumber(5.3014059295032) to the string `5.3014 ETH`', () => {
|
||||||
|
expect(format.ethUnitAmount(BIG_NUMBER_IRRATIONAL)).toBe('5.3014 ETH');
|
||||||
|
});
|
||||||
|
it('returns defaultText param when ethUnitAmount is not defined', () => {
|
||||||
|
const defaultText = 'defaultText';
|
||||||
|
expect(format.ethUnitAmount(undefined, 4, defaultText)).toBe(defaultText);
|
||||||
|
expect(format.ethUnitAmount(BIG_NUMBER_ONE, 4, defaultText)).toBe('1 ETH');
|
||||||
|
});
|
||||||
|
it('it allows for configurable decimal places', () => {
|
||||||
|
expect(format.ethUnitAmount(BIG_NUMBER_DECIMAL, 2)).toBe('0.43 ETH');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('ethBaseAmountInUsd', () => {
|
||||||
|
it('correctly formats 1 ETH to usd according to some price', () => {
|
||||||
|
expect(format.ethBaseAmountInUsd(ONE_ETH_IN_BASE_UNITS, BIG_NUMBER_FAKE_ETH_USD_PRICE)).toBe('$2.53');
|
||||||
|
});
|
||||||
|
it('correctly formats .432414 ETH to usd according to some price', () => {
|
||||||
|
expect(format.ethBaseAmountInUsd(DECIMAL_ETH_IN_BASE_UNITS, BIG_NUMBER_FAKE_ETH_USD_PRICE)).toBe('$1.10');
|
||||||
|
});
|
||||||
|
it('correctly formats 5.3014059295032 ETH to usd according to some price', () => {
|
||||||
|
expect(format.ethBaseAmountInUsd(IRRATIONAL_ETH_IN_BASE_UNITS, BIG_NUMBER_FAKE_ETH_USD_PRICE)).toBe(
|
||||||
|
'$13.43',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('returns defaultText param when ethBaseAmountInUsd or ethUsdPrice is not defined', () => {
|
||||||
|
const defaultText = 'defaultText';
|
||||||
|
expect(format.ethBaseAmountInUsd(undefined, undefined, 2, defaultText)).toBe(defaultText);
|
||||||
|
expect(format.ethBaseAmountInUsd(BIG_NUMBER_ONE, undefined, 2, defaultText)).toBe(defaultText);
|
||||||
|
expect(format.ethBaseAmountInUsd(undefined, BIG_NUMBER_ONE, 2, defaultText)).toBe(defaultText);
|
||||||
|
});
|
||||||
|
it('it allows for configurable decimal places', () => {
|
||||||
|
expect(format.ethBaseAmountInUsd(DECIMAL_ETH_IN_BASE_UNITS, BIG_NUMBER_FAKE_ETH_USD_PRICE, 4)).toBe(
|
||||||
|
'$1.0957',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('ethUnitAmountInUsd', () => {
|
||||||
|
it('correctly formats 1 ETH to usd according to some price', () => {
|
||||||
|
expect(format.ethUnitAmountInUsd(BIG_NUMBER_ONE, BIG_NUMBER_FAKE_ETH_USD_PRICE)).toBe('$2.53');
|
||||||
|
});
|
||||||
|
it('correctly formats .432414 ETH to usd according to some price', () => {
|
||||||
|
expect(format.ethUnitAmountInUsd(BIG_NUMBER_DECIMAL, BIG_NUMBER_FAKE_ETH_USD_PRICE)).toBe('$1.10');
|
||||||
|
});
|
||||||
|
it('correctly formats 5.3014059295032 ETH to usd according to some price', () => {
|
||||||
|
expect(format.ethUnitAmountInUsd(BIG_NUMBER_IRRATIONAL, BIG_NUMBER_FAKE_ETH_USD_PRICE)).toBe('$13.43');
|
||||||
|
});
|
||||||
|
it('returns defaultText param when ethUnitAmountInUsd or ethUsdPrice is not defined', () => {
|
||||||
|
const defaultText = 'defaultText';
|
||||||
|
expect(format.ethUnitAmountInUsd(undefined, undefined, 2, defaultText)).toBe(defaultText);
|
||||||
|
expect(format.ethUnitAmountInUsd(BIG_NUMBER_ONE, undefined, 2, defaultText)).toBe(defaultText);
|
||||||
|
expect(format.ethUnitAmountInUsd(undefined, BIG_NUMBER_ONE, 2, defaultText)).toBe(defaultText);
|
||||||
|
});
|
||||||
|
it('it allows for configurable decimal places', () => {
|
||||||
|
expect(format.ethUnitAmountInUsd(BIG_NUMBER_DECIMAL, BIG_NUMBER_FAKE_ETH_USD_PRICE, 4)).toBe('$1.0957');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -1,4 +1,14 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note":
|
||||||
|
"Convert all schemas to JSON files so that they can be used with `json-schema` implemenations in other programming languages.",
|
||||||
|
"pr": 1145
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1538693146,
|
"timestamp": 1538693146,
|
||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc -b",
|
"build": "tsc -b",
|
||||||
"build:ci": "yarn build",
|
"build:ci": "yarn build",
|
||||||
"lint": "tslint --project .",
|
"lint": "tslint --project . --exclude **/schemas/**/*",
|
||||||
"test": "yarn run_mocha",
|
"test": "yarn run_mocha",
|
||||||
"rebuild_and_test": "run-s clean build test",
|
"rebuild_and_test": "run-s clean build test",
|
||||||
"test:coverage": "nyc npm run test --all && yarn coverage:report:lcov",
|
"test:coverage": "nyc npm run test --all && yarn coverage:report:lcov",
|
||||||
@ -59,7 +59,7 @@
|
|||||||
"nyc": "^11.0.1",
|
"nyc": "^11.0.1",
|
||||||
"shx": "^0.2.2",
|
"shx": "^0.2.2",
|
||||||
"tslint": "5.11.0",
|
"tslint": "5.11.0",
|
||||||
"typedoc": "0.12.0",
|
"typedoc": "0.13.0",
|
||||||
"typescript": "3.0.1"
|
"typescript": "3.0.1"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
|
5
packages/json-schemas/schemas/address_schema.json
Normal file
5
packages/json-schemas/schemas/address_schema.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"id": "/addressSchema",
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^0x[0-9a-f]{40}$"
|
||||||
|
}
|
@ -1,17 +0,0 @@
|
|||||||
export const addressSchema = {
|
|
||||||
id: '/addressSchema',
|
|
||||||
type: 'string',
|
|
||||||
pattern: '^0x[0-9a-f]{40}$',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const hexSchema = {
|
|
||||||
id: '/hexSchema',
|
|
||||||
type: 'string',
|
|
||||||
pattern: '^0x(([0-9a-f][0-9a-f])+)?$',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const numberSchema = {
|
|
||||||
id: '/numberSchema',
|
|
||||||
type: 'string',
|
|
||||||
pattern: '^\\d+(\\.\\d+)?$',
|
|
||||||
};
|
|
11
packages/json-schemas/schemas/block_param_schema.json
Normal file
11
packages/json-schemas/schemas/block_param_schema.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"id": "/blockParamSchema",
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"enum": ["latest", "earliest", "pending"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
8
packages/json-schemas/schemas/block_range_schema.json
Normal file
8
packages/json-schemas/schemas/block_range_schema.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"id": "/blockRangeSchema",
|
||||||
|
"properties": {
|
||||||
|
"fromBlock": { "$ref": "/blockParamSchema" },
|
||||||
|
"toBlock": { "$ref": "/blockParamSchema" }
|
||||||
|
},
|
||||||
|
"type": "object"
|
||||||
|
}
|
@ -1,20 +0,0 @@
|
|||||||
export const blockParamSchema = {
|
|
||||||
id: '/blockParamSchema',
|
|
||||||
oneOf: [
|
|
||||||
{
|
|
||||||
type: 'number',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
enum: ['latest', 'earliest', 'pending'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export const blockRangeSchema = {
|
|
||||||
id: '/blockRangeSchema',
|
|
||||||
properties: {
|
|
||||||
fromBlock: { $ref: '/blockParamSchema' },
|
|
||||||
toBlock: { $ref: '/blockParamSchema' },
|
|
||||||
},
|
|
||||||
type: 'object',
|
|
||||||
};
|
|
27
packages/json-schemas/schemas/call_data_schema.json
Normal file
27
packages/json-schemas/schemas/call_data_schema.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"id": "/callDataSchema",
|
||||||
|
"properties": {
|
||||||
|
"from": { "$ref": "/addressSchema" },
|
||||||
|
"to": { "$ref": "/addressSchema" },
|
||||||
|
"value": {
|
||||||
|
"oneOf": [{ "$ref": "/numberSchema" }, { "$ref": "/jsNumber" }]
|
||||||
|
},
|
||||||
|
"gas": {
|
||||||
|
"oneOf": [{ "$ref": "/numberSchema" }, { "$ref": "/jsNumber" }]
|
||||||
|
},
|
||||||
|
"gasPrice": {
|
||||||
|
"oneOf": [{ "$ref": "/numberSchema" }, { "$ref": "/jsNumber" }]
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^0x[0-9a-f]*$"
|
||||||
|
},
|
||||||
|
"nonce": {
|
||||||
|
"type": "number",
|
||||||
|
"minimum": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [],
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
@ -1,27 +0,0 @@
|
|||||||
export const callDataSchema = {
|
|
||||||
id: '/callDataSchema',
|
|
||||||
properties: {
|
|
||||||
from: { $ref: '/addressSchema' },
|
|
||||||
to: { $ref: '/addressSchema' },
|
|
||||||
value: {
|
|
||||||
oneOf: [{ $ref: '/numberSchema' }, { $ref: '/jsNumber' }],
|
|
||||||
},
|
|
||||||
gas: {
|
|
||||||
oneOf: [{ $ref: '/numberSchema' }, { $ref: '/jsNumber' }],
|
|
||||||
},
|
|
||||||
gasPrice: {
|
|
||||||
oneOf: [{ $ref: '/numberSchema' }, { $ref: '/jsNumber' }],
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
type: 'string',
|
|
||||||
pattern: '^0x[0-9a-f]*$',
|
|
||||||
},
|
|
||||||
nonce: {
|
|
||||||
type: 'number',
|
|
||||||
minimum: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
required: [],
|
|
||||||
type: 'object',
|
|
||||||
additionalProperties: false,
|
|
||||||
};
|
|
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"id": "/ecSignatureParameterSchema",
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^0[xX][0-9A-Fa-f]{64}$"
|
||||||
|
}
|
14
packages/json-schemas/schemas/ec_signature_schema.json
Normal file
14
packages/json-schemas/schemas/ec_signature_schema.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"id": "/ECSignature",
|
||||||
|
"properties": {
|
||||||
|
"v": {
|
||||||
|
"type": "number",
|
||||||
|
"minimum": 27,
|
||||||
|
"maximum": 28
|
||||||
|
},
|
||||||
|
"r": { "$ref": "/ecSignatureParameterSchema" },
|
||||||
|
"s": { "$ref": "/ecSignatureParameterSchema" }
|
||||||
|
},
|
||||||
|
"required": ["v", "r", "s"],
|
||||||
|
"type": "object"
|
||||||
|
}
|
@ -1,20 +0,0 @@
|
|||||||
export const ecSignatureParameterSchema = {
|
|
||||||
id: '/ecSignatureParameterSchema',
|
|
||||||
type: 'string',
|
|
||||||
pattern: '^0[xX][0-9A-Fa-f]{64}$',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ecSignatureSchema = {
|
|
||||||
id: '/ECSignature',
|
|
||||||
properties: {
|
|
||||||
v: {
|
|
||||||
type: 'number',
|
|
||||||
minimum: 27,
|
|
||||||
maximum: 28,
|
|
||||||
},
|
|
||||||
r: { $ref: '/ecSignatureParameterSchema' },
|
|
||||||
s: { $ref: '/ecSignatureParameterSchema' },
|
|
||||||
},
|
|
||||||
required: ['v', 'r', 's'],
|
|
||||||
type: 'object',
|
|
||||||
};
|
|
@ -1,28 +0,0 @@
|
|||||||
export const eip712TypedDataSchema = {
|
|
||||||
id: '/eip712TypedData',
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
types: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
EIP712Domain: { type: 'array' },
|
|
||||||
},
|
|
||||||
additionalProperties: {
|
|
||||||
type: 'array',
|
|
||||||
items: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
name: { type: 'string' },
|
|
||||||
type: { type: 'string' },
|
|
||||||
},
|
|
||||||
required: ['name', 'type'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
required: ['EIP712Domain'],
|
|
||||||
},
|
|
||||||
primaryType: { type: 'string' },
|
|
||||||
domain: { type: 'object' },
|
|
||||||
message: { type: 'object' },
|
|
||||||
},
|
|
||||||
required: ['types', 'primaryType', 'domain', 'message'],
|
|
||||||
};
|
|
28
packages/json-schemas/schemas/eip712_typed_data_schema.json
Normal file
28
packages/json-schemas/schemas/eip712_typed_data_schema.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"id": "/eip712TypedDataSchema",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"types": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"EIP712Domain": { "type": "array" }
|
||||||
|
},
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": { "type": "string" },
|
||||||
|
"type": { "type": "string" }
|
||||||
|
},
|
||||||
|
"required": ["name", "type"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["EIP712Domain"]
|
||||||
|
},
|
||||||
|
"primaryType": { "type": "string" },
|
||||||
|
"domain": { "type": "object" },
|
||||||
|
"message": { "type": "object" }
|
||||||
|
},
|
||||||
|
"required": ["types", "primaryType", "domain", "message"]
|
||||||
|
}
|
5
packages/json-schemas/schemas/hex_schema.json
Normal file
5
packages/json-schemas/schemas/hex_schema.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"id": "/hexSchema",
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^0x(([0-9a-f][0-9a-f])+)?$"
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"id": "/indexFilterValuesSchema",
|
||||||
|
"additionalProperties": {
|
||||||
|
"oneOf": [{ "$ref": "/numberSchema" }, { "$ref": "/addressSchema" }, { "$ref": "/orderHashSchema" }]
|
||||||
|
},
|
||||||
|
"type": "object"
|
||||||
|
}
|
@ -1,7 +0,0 @@
|
|||||||
export const indexFilterValuesSchema = {
|
|
||||||
id: '/indexFilterValuesSchema',
|
|
||||||
additionalProperties: {
|
|
||||||
oneOf: [{ $ref: '/numberSchema' }, { $ref: '/addressSchema' }, { $ref: '/orderHashSchema' }],
|
|
||||||
},
|
|
||||||
type: 'object',
|
|
||||||
};
|
|
5
packages/json-schemas/schemas/js_number.json
Normal file
5
packages/json-schemas/schemas/js_number.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"id": "/jsNumber",
|
||||||
|
"type": "number",
|
||||||
|
"minimum": 0
|
||||||
|
}
|
5
packages/json-schemas/schemas/number_schema.json
Normal file
5
packages/json-schemas/schemas/number_schema.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"id": "/numberSchema",
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^\\d+(\\.\\d+)?$"
|
||||||
|
}
|
12
packages/json-schemas/schemas/order_cancel_schema.json
Normal file
12
packages/json-schemas/schemas/order_cancel_schema.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"id": "/orderCancellationRequestsSchema",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"properties": {
|
||||||
|
"order": { "$ref": "/orderSchema" },
|
||||||
|
"takerTokenCancelAmount": { "$ref": "/numberSchema" }
|
||||||
|
},
|
||||||
|
"required": ["order", "takerTokenCancelAmount"],
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +0,0 @@
|
|||||||
export const orderCancellationRequestsSchema = {
|
|
||||||
id: '/orderCancellationRequestsSchema',
|
|
||||||
type: 'array',
|
|
||||||
items: {
|
|
||||||
properties: {
|
|
||||||
order: { $ref: '/orderSchema' },
|
|
||||||
takerTokenCancelAmount: { $ref: '/numberSchema' },
|
|
||||||
},
|
|
||||||
required: ['order', 'takerTokenCancelAmount'],
|
|
||||||
type: 'object',
|
|
||||||
},
|
|
||||||
};
|
|
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"id": "/orderFillOrKillRequestsSchema",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"properties": {
|
||||||
|
"signedOrder": { "$ref": "/signedOrderSchema" },
|
||||||
|
"fillTakerAmount": { "$ref": "/numberSchema" }
|
||||||
|
},
|
||||||
|
"required": ["signedOrder", "fillTakerAmount"],
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +0,0 @@
|
|||||||
export const orderFillOrKillRequestsSchema = {
|
|
||||||
id: '/orderFillOrKillRequestsSchema',
|
|
||||||
type: 'array',
|
|
||||||
items: {
|
|
||||||
properties: {
|
|
||||||
signedOrder: { $ref: '/signedOrderSchema' },
|
|
||||||
fillTakerAmount: { $ref: '/numberSchema' },
|
|
||||||
},
|
|
||||||
required: ['signedOrder', 'fillTakerAmount'],
|
|
||||||
type: 'object',
|
|
||||||
},
|
|
||||||
};
|
|
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"id": "/orderFillRequestsSchema",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"properties": {
|
||||||
|
"signedOrder": { "$ref": "/signedOrderSchema" },
|
||||||
|
"takerTokenFillAmount": { "$ref": "/numberSchema" }
|
||||||
|
},
|
||||||
|
"required": ["signedOrder", "takerTokenFillAmount"],
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +0,0 @@
|
|||||||
export const orderFillRequestsSchema = {
|
|
||||||
id: '/orderFillRequestsSchema',
|
|
||||||
type: 'array',
|
|
||||||
items: {
|
|
||||||
properties: {
|
|
||||||
signedOrder: { $ref: '/signedOrderSchema' },
|
|
||||||
takerTokenFillAmount: { $ref: '/numberSchema' },
|
|
||||||
},
|
|
||||||
required: ['signedOrder', 'takerTokenFillAmount'],
|
|
||||||
type: 'object',
|
|
||||||
},
|
|
||||||
};
|
|
5
packages/json-schemas/schemas/order_hash_schema.json
Normal file
5
packages/json-schemas/schemas/order_hash_schema.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"id": "/orderHashSchema",
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^0x[0-9a-fA-F]{64}$"
|
||||||
|
}
|
@ -1,5 +0,0 @@
|
|||||||
export const orderHashSchema = {
|
|
||||||
id: '/orderHashSchema',
|
|
||||||
type: 'string',
|
|
||||||
pattern: '^0x[0-9a-fA-F]{64}$',
|
|
||||||
};
|
|
34
packages/json-schemas/schemas/order_schema.json
Normal file
34
packages/json-schemas/schemas/order_schema.json
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"id": "/orderSchema",
|
||||||
|
"properties": {
|
||||||
|
"makerAddress": { "$ref": "/addressSchema" },
|
||||||
|
"takerAddress": { "$ref": "/addressSchema" },
|
||||||
|
"makerFee": { "$ref": "/numberSchema" },
|
||||||
|
"takerFee": { "$ref": "/numberSchema" },
|
||||||
|
"senderAddress": { "$ref": "/addressSchema" },
|
||||||
|
"makerAssetAmount": { "$ref": "/numberSchema" },
|
||||||
|
"takerAssetAmount": { "$ref": "/numberSchema" },
|
||||||
|
"makerAssetData": { "$ref": "/hexSchema" },
|
||||||
|
"takerAssetData": { "$ref": "/hexSchema" },
|
||||||
|
"salt": { "$ref": "/numberSchema" },
|
||||||
|
"exchangeAddress": { "$ref": "/addressSchema" },
|
||||||
|
"feeRecipientAddress": { "$ref": "/addressSchema" },
|
||||||
|
"expirationTimeSeconds": { "$ref": "/numberSchema" }
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"makerAddress",
|
||||||
|
"takerAddress",
|
||||||
|
"makerFee",
|
||||||
|
"takerFee",
|
||||||
|
"senderAddress",
|
||||||
|
"makerAssetAmount",
|
||||||
|
"takerAssetAmount",
|
||||||
|
"makerAssetData",
|
||||||
|
"takerAssetData",
|
||||||
|
"salt",
|
||||||
|
"exchangeAddress",
|
||||||
|
"feeRecipientAddress",
|
||||||
|
"expirationTimeSeconds"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
}
|
@ -1,47 +0,0 @@
|
|||||||
export const orderSchema = {
|
|
||||||
id: '/orderSchema',
|
|
||||||
properties: {
|
|
||||||
makerAddress: { $ref: '/addressSchema' },
|
|
||||||
takerAddress: { $ref: '/addressSchema' },
|
|
||||||
makerFee: { $ref: '/numberSchema' },
|
|
||||||
takerFee: { $ref: '/numberSchema' },
|
|
||||||
senderAddress: { $ref: '/addressSchema' },
|
|
||||||
makerAssetAmount: { $ref: '/numberSchema' },
|
|
||||||
takerAssetAmount: { $ref: '/numberSchema' },
|
|
||||||
makerAssetData: { $ref: '/hexSchema' },
|
|
||||||
takerAssetData: { $ref: '/hexSchema' },
|
|
||||||
salt: { $ref: '/numberSchema' },
|
|
||||||
exchangeAddress: { $ref: '/addressSchema' },
|
|
||||||
feeRecipientAddress: { $ref: '/addressSchema' },
|
|
||||||
expirationTimeSeconds: { $ref: '/numberSchema' },
|
|
||||||
},
|
|
||||||
required: [
|
|
||||||
'makerAddress',
|
|
||||||
'takerAddress',
|
|
||||||
'makerFee',
|
|
||||||
'takerFee',
|
|
||||||
'senderAddress',
|
|
||||||
'makerAssetAmount',
|
|
||||||
'takerAssetAmount',
|
|
||||||
'makerAssetData',
|
|
||||||
'takerAssetData',
|
|
||||||
'salt',
|
|
||||||
'exchangeAddress',
|
|
||||||
'feeRecipientAddress',
|
|
||||||
'expirationTimeSeconds',
|
|
||||||
],
|
|
||||||
type: 'object',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const signedOrderSchema = {
|
|
||||||
id: '/signedOrderSchema',
|
|
||||||
allOf: [
|
|
||||||
{ $ref: '/orderSchema' },
|
|
||||||
{
|
|
||||||
properties: {
|
|
||||||
signature: { $ref: '/hexSchema' },
|
|
||||||
},
|
|
||||||
required: ['signature'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
5
packages/json-schemas/schemas/orders_schema.json
Normal file
5
packages/json-schemas/schemas/orders_schema.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"id": "/ordersSchema",
|
||||||
|
"type": "array",
|
||||||
|
"items": { "$ref": "/orderSchema" }
|
||||||
|
}
|
@ -1,5 +0,0 @@
|
|||||||
export const ordersSchema = {
|
|
||||||
id: '/ordersSchema',
|
|
||||||
type: 'array',
|
|
||||||
items: { $ref: '/orderSchema' },
|
|
||||||
};
|
|
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"id": "/paginatedCollectionSchema",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"total": { "type": "number" },
|
||||||
|
"perPage": { "type": "number" },
|
||||||
|
"page": { "type": "number" }
|
||||||
|
},
|
||||||
|
"required": ["total", "perPage", "page"]
|
||||||
|
}
|
@ -1,10 +0,0 @@
|
|||||||
export const paginatedCollectionSchema = {
|
|
||||||
id: '/paginatedCollectionSchema',
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
total: { type: 'number' },
|
|
||||||
perPage: { type: 'number' },
|
|
||||||
page: { type: 'number' },
|
|
||||||
},
|
|
||||||
required: ['total', 'perPage', 'page'],
|
|
||||||
};
|
|
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"id": "/relayerApiAssetDataPairsResponseSchema",
|
||||||
|
"type": "object",
|
||||||
|
"allOf": [
|
||||||
|
{ "$ref": "/paginatedCollectionSchema" },
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"records": { "$ref": "/relayerApiAssetDataPairsSchema" }
|
||||||
|
},
|
||||||
|
"required": ["records"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"id": "/relayerApiAssetDataPairsSchema",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"properties": {
|
||||||
|
"assetDataA": { "$ref": "/relayerApiAssetDataTradeInfoSchema" },
|
||||||
|
"assetDataB": { "$ref": "/relayerApiAssetDataTradeInfoSchema" }
|
||||||
|
},
|
||||||
|
"required": ["assetDataA", "assetDataB"],
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"id": "/relayerApiAssetDataTradeInfoSchema",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"assetData": { "$ref": "/hexSchema" },
|
||||||
|
"minAmount": { "$ref": "/numberSchema" },
|
||||||
|
"maxAmount": { "$ref": "/numberSchema" },
|
||||||
|
"precision": { "type": "number" }
|
||||||
|
},
|
||||||
|
"required": ["assetData"]
|
||||||
|
}
|
@ -1,38 +0,0 @@
|
|||||||
export const relayerApiAssetDataPairsResponseSchema = {
|
|
||||||
id: '/relayerApiAssetDataPairsResponseSchema',
|
|
||||||
type: 'object',
|
|
||||||
allOf: [
|
|
||||||
{ $ref: '/paginatedCollectionSchema' },
|
|
||||||
{
|
|
||||||
properties: {
|
|
||||||
records: { $ref: '/relayerApiAssetDataPairsSchema' },
|
|
||||||
},
|
|
||||||
required: ['records'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export const relayerApiAssetDataPairsSchema = {
|
|
||||||
id: '/relayerApiAssetDataPairsSchema',
|
|
||||||
type: 'array',
|
|
||||||
items: {
|
|
||||||
properties: {
|
|
||||||
assetDataA: { $ref: '/relayerApiAssetDataTradeInfoSchema' },
|
|
||||||
assetDataB: { $ref: '/relayerApiAssetDataTradeInfoSchema' },
|
|
||||||
},
|
|
||||||
required: ['assetDataA', 'assetDataB'],
|
|
||||||
type: 'object',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const relayerApiAssetDataTradeInfoSchema = {
|
|
||||||
id: '/relayerApiAssetDataTradeInfoSchema',
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
assetData: { $ref: '/hexSchema' },
|
|
||||||
minAmount: { $ref: '/numberSchema' },
|
|
||||||
maxAmount: { $ref: '/numberSchema' },
|
|
||||||
precision: { type: 'number' },
|
|
||||||
},
|
|
||||||
required: ['assetData'],
|
|
||||||
};
|
|
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"id": "/relayerApiErrorResponseSchema",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"code": { "type": "integer", "minimum": 100, "maximum": 103 },
|
||||||
|
"reason": { "type": "string" },
|
||||||
|
"validationErrors": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"field": { "type": "string" },
|
||||||
|
"code": { "type": "integer", "minimum": 1000, "maximum": 1006 },
|
||||||
|
"reason": { "type": "string" }
|
||||||
|
},
|
||||||
|
"required": ["field", "code", "reason"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["code", "reason"]
|
||||||
|
}
|
@ -1,21 +0,0 @@
|
|||||||
export const relayerApiErrorResponseSchema = {
|
|
||||||
id: '/relayerApiErrorResponseSchema',
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
code: { type: 'integer', minimum: 100, maximum: 103 },
|
|
||||||
reason: { type: 'string' },
|
|
||||||
validationErrors: {
|
|
||||||
type: 'array',
|
|
||||||
items: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
field: { type: 'string' },
|
|
||||||
code: { type: 'integer', minimum: 1000, maximum: 1006 },
|
|
||||||
reason: { type: 'string' },
|
|
||||||
},
|
|
||||||
required: ['field', 'code', 'reason'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
required: ['code', 'reason'],
|
|
||||||
};
|
|
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"id": "/relayerApiFeeRecipientsResponseSchema",
|
||||||
|
"type": "object",
|
||||||
|
"allOf": [
|
||||||
|
{ "$ref": "/paginatedCollectionSchema" },
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"records": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "$ref": "/addressSchema" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["records"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -1,16 +0,0 @@
|
|||||||
export const relayerApiFeeRecipientsResponseSchema = {
|
|
||||||
id: '/relayerApiFeeRecipientsResponseSchema',
|
|
||||||
type: 'object',
|
|
||||||
allOf: [
|
|
||||||
{ $ref: '/paginatedCollectionSchema' },
|
|
||||||
{
|
|
||||||
properties: {
|
|
||||||
records: {
|
|
||||||
type: 'array',
|
|
||||||
items: { $ref: '/addressSchema' },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
required: ['records'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"id": "/relayerApiOrderConfigPayloadSchema",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"makerAddress": { "$ref": "/addressSchema" },
|
||||||
|
"takerAddress": { "$ref": "/addressSchema" },
|
||||||
|
"makerAssetAmount": { "$ref": "/numberSchema" },
|
||||||
|
"takerAssetAmount": { "$ref": "/numberSchema" },
|
||||||
|
"makerAssetData": { "$ref": "/hexSchema" },
|
||||||
|
"takerAssetData": { "$ref": "/hexSchema" },
|
||||||
|
"exchangeAddress": { "$ref": "/addressSchema" },
|
||||||
|
"expirationTimeSeconds": { "$ref": "/numberSchema" }
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"makerAddress",
|
||||||
|
"takerAddress",
|
||||||
|
"makerAssetAmount",
|
||||||
|
"takerAssetAmount",
|
||||||
|
"makerAssetData",
|
||||||
|
"takerAssetData",
|
||||||
|
"exchangeAddress",
|
||||||
|
"expirationTimeSeconds"
|
||||||
|
]
|
||||||
|
}
|
@ -1,24 +0,0 @@
|
|||||||
export const relayerApiOrderConfigPayloadSchema = {
|
|
||||||
id: '/relayerApiOrderConfigPayloadSchema',
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
makerAddress: { $ref: '/addressSchema' },
|
|
||||||
takerAddress: { $ref: '/addressSchema' },
|
|
||||||
makerAssetAmount: { $ref: '/numberSchema' },
|
|
||||||
takerAssetAmount: { $ref: '/numberSchema' },
|
|
||||||
makerAssetData: { $ref: '/hexSchema' },
|
|
||||||
takerAssetData: { $ref: '/hexSchema' },
|
|
||||||
exchangeAddress: { $ref: '/addressSchema' },
|
|
||||||
expirationTimeSeconds: { $ref: '/numberSchema' },
|
|
||||||
},
|
|
||||||
required: [
|
|
||||||
'makerAddress',
|
|
||||||
'takerAddress',
|
|
||||||
'makerAssetAmount',
|
|
||||||
'takerAssetAmount',
|
|
||||||
'makerAssetData',
|
|
||||||
'takerAssetData',
|
|
||||||
'exchangeAddress',
|
|
||||||
'expirationTimeSeconds',
|
|
||||||
],
|
|
||||||
};
|
|
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"id": "/relayerApiOrderConfigResponseSchema",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"makerFee": { "$ref": "/numberSchema" },
|
||||||
|
"takerFee": { "$ref": "/numberSchema" },
|
||||||
|
"feeRecipientAddress": { "$ref": "/addressSchema" },
|
||||||
|
"senderAddress": { "$ref": "/addressSchema" }
|
||||||
|
},
|
||||||
|
"required": ["makerFee", "takerFee", "feeRecipientAddress", "senderAddress"]
|
||||||
|
}
|
@ -1,11 +0,0 @@
|
|||||||
export const relayerApiOrderConfigResponseSchema = {
|
|
||||||
id: '/relayerApiOrderConfigResponseSchema',
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
makerFee: { $ref: '/numberSchema' },
|
|
||||||
takerFee: { $ref: '/numberSchema' },
|
|
||||||
feeRecipientAddress: { $ref: '/addressSchema' },
|
|
||||||
senderAddress: { $ref: '/addressSchema' },
|
|
||||||
},
|
|
||||||
required: ['makerFee', 'takerFee', 'feeRecipientAddress', 'senderAddress'],
|
|
||||||
};
|
|
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"id": "/relayerApiOrderSchema",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"order": { "$ref": "/orderSchema" },
|
||||||
|
"metaData": { "type": "object" }
|
||||||
|
},
|
||||||
|
"required": ["order", "metaData"]
|
||||||
|
}
|
@ -1,9 +0,0 @@
|
|||||||
export const relayerApiOrderSchema = {
|
|
||||||
id: '/relayerApiOrderSchema',
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
order: { $ref: '/orderSchema' },
|
|
||||||
metaData: { type: 'object' },
|
|
||||||
},
|
|
||||||
required: ['order', 'metaData'],
|
|
||||||
};
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user