aboutsummaryrefslogtreecommitdiffstats
path: root/packages/website
diff options
context:
space:
mode:
authorFabio Berger <me@fabioberger.com>2018-06-02 04:05:17 +0800
committerFabio Berger <me@fabioberger.com>2018-06-02 04:05:17 +0800
commitb7b45b69a66cfaf9c3737d1a8d95be21edccf527 (patch)
tree4337613836a8062bd73fc8ee1e4db444961df172 /packages/website
parentdf9cfe7840b99d72ed95058e47f2ffb6623d440e (diff)
parent9ca41b9536908dddfa51854abd41b7926e69bd09 (diff)
downloaddexon-sol-tools-b7b45b69a66cfaf9c3737d1a8d95be21edccf527.tar
dexon-sol-tools-b7b45b69a66cfaf9c3737d1a8d95be21edccf527.tar.gz
dexon-sol-tools-b7b45b69a66cfaf9c3737d1a8d95be21edccf527.tar.bz2
dexon-sol-tools-b7b45b69a66cfaf9c3737d1a8d95be21edccf527.tar.lz
dexon-sol-tools-b7b45b69a66cfaf9c3737d1a8d95be21edccf527.tar.xz
dexon-sol-tools-b7b45b69a66cfaf9c3737d1a8d95be21edccf527.tar.zst
dexon-sol-tools-b7b45b69a66cfaf9c3737d1a8d95be21edccf527.zip
Merge branch 'v2-prototype' into refactor/order-utils/for-v2
* v2-prototype: (33 commits) Only show ProviderDisplay in portal Improve sol-cov docs Remove old parse code Refactor order parser and add shared order support to new portal Add generate and fill order routes Address feedback Override ethereumjs-tx version Fix missing key Update placeholder param ordering Change userEtherBalanceInWei to optional so we can know if its loading Add loading state to ProviderDisplay Tweaks Add Placeholder component Add StandardIconRow Split render into loading and loaaded Fix linter errors Fix linter errors Add ethereum-types to extraFileIncludes Introduce ethereum-types package Remove merge conflicts from yarn.lock ... # Conflicts: # packages/contracts/src/utils/exchange_wrapper.ts # packages/contracts/src/utils/match_order_tester.ts # packages/contracts/src/utils/types.ts # packages/contracts/test/exchange/core.ts # packages/contracts/test/exchange/match_orders.ts # packages/contracts/test/libraries/lib_bytes.ts # packages/sol-cov/package.json
Diffstat (limited to 'packages/website')
-rw-r--r--packages/website/md/docs/sol_cov/usage.md48
-rw-r--r--packages/website/package.json4
-rw-r--r--packages/website/ts/blockchain_watcher.ts6
-rw-r--r--packages/website/ts/components/eth_weth_conversion_button.tsx2
-rw-r--r--packages/website/ts/components/eth_wrappers.tsx2
-rw-r--r--packages/website/ts/components/legacy_portal/legacy_portal.tsx31
-rw-r--r--packages/website/ts/components/onboarding/portal_onboarding_flow.tsx4
-rw-r--r--packages/website/ts/components/portal/menu.tsx11
-rw-r--r--packages/website/ts/components/portal/portal.tsx38
-rw-r--r--packages/website/ts/components/token_balances.tsx3
-rw-r--r--packages/website/ts/components/top_bar/provider_display.tsx36
-rw-r--r--packages/website/ts/components/top_bar/top_bar.tsx3
-rw-r--r--packages/website/ts/components/wallet/wallet.tsx191
-rw-r--r--packages/website/ts/components/wallet/wallet_disconnected_item.tsx2
-rw-r--r--packages/website/ts/containers/legacy_portal.ts2
-rw-r--r--packages/website/ts/containers/portal.ts2
-rw-r--r--packages/website/ts/containers/portal_onboarding_flow.ts4
-rw-r--r--packages/website/ts/containers/sol_cov_documentation.ts36
-rw-r--r--packages/website/ts/redux/dispatcher.ts2
-rw-r--r--packages/website/ts/redux/reducer.ts4
-rw-r--r--packages/website/ts/utils/order_parser.ts33
21 files changed, 341 insertions, 123 deletions
diff --git a/packages/website/md/docs/sol_cov/usage.md b/packages/website/md/docs/sol_cov/usage.md
index 63a88f595..59638f611 100644
--- a/packages/website/md/docs/sol_cov/usage.md
+++ b/packages/website/md/docs/sol_cov/usage.md
@@ -2,8 +2,47 @@ Sol-cov uses transaction traces in order to figure out which lines of Solidity s
The CoverageSubprovider eavesdrops on the `eth_sendTransaction` and `eth_call` RPC calls and collects traces after each call using `debug_traceTransaction`. `eth_call`'s' don't generate traces - so we take a snapshot, re-submit it as a transaction, get the trace and then revert the snapshot.
+Coverage subprovider needs some info about your contracts (`srcMap`, `bytecode`). It gets that info from your project's artifacts. Some frameworks have their own artifact format. Some artifact formats don't actually contain all the neccessary data.
+
+In order to use `CoverageSubprovider` with your favorite framework you need to pass an `artifactsAdapter` to it.
+
+### Sol-compiler
+
+If you are generating your artifacts with [@0xproject/sol-compiler](LINK) you can use the `SolCompilerArtifactsAdapter` we've implemented for you.
+
+```typescript
+<<<<<<< HEAD
+import { CoverageSubprovider } from '@0xproject/sol-cov';
+=======
+import { SolCompilerArtifactsAdapter } from '@0xproject/sol-cov';
+const artifactsPath = 'src/artifacts';
+const contractsPath = 'src/contracts';
+const artifactsAdapter = new SolCompilerArtifactsAdapter(artifactsPath, contractsPath);
+```
+
+### Truffle
+
+If your project is using [Truffle](LINK), we've written a `TruffleArtifactsAdapter`for you.
+
+```typescript
+import { TruffleArtifactAdapter } from '@0xproject/sol-cov';
+const contractsPath = 'src/contracts';
+const artifactAdapter = new TruffleArtifactAdapter(contractsDir);
+```
+
+Because truffle artifacts don't have all the data we need - we actually will recompile your contracts under the hood. That's why you don't need to pass an `artifactsPath`.
+
+### Other framework/toolset
+
+You'll need to write your own artifacts adapter. It should extend `AbstractArtifactsAdapter`.
+Look at the code of the two adapters above for examples.
+
+### Usage
+
```typescript
import { CoverageSubprovider } from '@0xproject/sol-cov';
+import ProviderEngine = require('web3-provider-engine');
+>>>>>>> Improve sol-cov docs
const provider = new ProviderEngine();
@@ -12,15 +51,20 @@ const contractsPath = 'src/contracts';
const networkId = 50;
// Some calls might not have `from` address specified. Nevertheless - transactions need to be submitted from an address with at least some funds. defaultFromAddress is the address that will be used to submit those calls as transactions from.
const defaultFromAddress = '0x5409ed021d9299bf6814279a6a1411a7e866a631';
+<<<<<<< HEAD
const coverageSubprovider = new CoverageSubprovider(artifactsPath, contractsPath, defaultFromAddress);
+=======
+const isVerbose = true;
+const coverageSubprovider = new CoverageSubprovider(artifactsAdapter, defaultFromAddress, isVerbose);
+>>>>>>> Improve sol-cov docs
provider.addProvider(coverageSubprovider);
```
-After your test suite is complete (e.g global `after` hook), you'll need to call:
+After your test suite is complete (e.g in the Mocha global `after` hook), you'll need to call:
```typescript
await coverageSubprovider.writeCoverageAsync();
```
-This will create a `coverage.json` file in the `coverage` directory. This file has an [Istanbul format](https://github.com/gotwarlost/istanbul/blob/master/coverage.json.md) - so you can use any of the existing Instanbul reporters.
+This will create a `coverage.json` file in a `coverage` directory. This file has an [Istanbul format](https://github.com/gotwarlost/istanbul/blob/master/coverage.json.md) - so you can use it with any of the existing Istanbul reporters.
diff --git a/packages/website/package.json b/packages/website/package.json
index 95804f988..a17964f2b 100644
--- a/packages/website/package.json
+++ b/packages/website/package.json
@@ -57,11 +57,13 @@
"web3": "^0.20.0",
"web3-provider-engine": "^14.0.4",
"whatwg-fetch": "^2.0.3",
- "xml-js": "^1.3.2"
+ "xml-js": "^1.6.4"
},
"devDependencies": {
"@types/accounting": "^0.4.1",
+ "@types/blockies": "^0.0.0",
"@types/deep-equal": "^1.0.0",
+ "@types/find-versions": "^2.0.0",
"@types/jsonschema": "^1.1.1",
"@types/lodash": "4.14.104",
"@types/material-ui": "0.18.0",
diff --git a/packages/website/ts/blockchain_watcher.ts b/packages/website/ts/blockchain_watcher.ts
index c420a98a4..0d376bc74 100644
--- a/packages/website/ts/blockchain_watcher.ts
+++ b/packages/website/ts/blockchain_watcher.ts
@@ -10,7 +10,7 @@ export class BlockchainWatcher {
private _prevNetworkId: number;
private _shouldPollUserAddress: boolean;
private _watchNetworkAndBalanceIntervalId: NodeJS.Timer;
- private _prevUserEtherBalanceInWei: BigNumber;
+ private _prevUserEtherBalanceInWei?: BigNumber;
private _prevUserAddressIfExists: string;
constructor(
dispatcher: Dispatcher,
@@ -41,7 +41,7 @@ export class BlockchainWatcher {
}
let prevNodeVersion: string;
- this._prevUserEtherBalanceInWei = new BigNumber(0);
+ this._prevUserEtherBalanceInWei = undefined;
this._dispatcher.updateNetworkId(this._prevNetworkId);
this._watchNetworkAndBalanceIntervalId = intervalUtils.setAsyncExcludingInterval(
async () => {
@@ -94,7 +94,7 @@ export class BlockchainWatcher {
}
private async _updateUserWeiBalanceAsync(userAddress: string): Promise<void> {
const balanceInWei = await this._web3Wrapper.getBalanceInWeiAsync(userAddress);
- if (!balanceInWei.eq(this._prevUserEtherBalanceInWei)) {
+ if (_.isUndefined(this._prevUserEtherBalanceInWei) || !balanceInWei.eq(this._prevUserEtherBalanceInWei)) {
this._prevUserEtherBalanceInWei = balanceInWei;
this._dispatcher.updateUserWeiBalance(balanceInWei);
}
diff --git a/packages/website/ts/components/eth_weth_conversion_button.tsx b/packages/website/ts/components/eth_weth_conversion_button.tsx
index 4b91a2ebd..2fb35cc1c 100644
--- a/packages/website/ts/components/eth_weth_conversion_button.tsx
+++ b/packages/website/ts/components/eth_weth_conversion_button.tsx
@@ -18,7 +18,7 @@ interface EthWethConversionButtonProps {
ethToken: Token;
dispatcher: Dispatcher;
blockchain: Blockchain;
- userEtherBalanceInWei: BigNumber;
+ userEtherBalanceInWei?: BigNumber;
isOutdatedWrappedEther: boolean;
onConversionSuccessful?: () => void;
isDisabled?: boolean;
diff --git a/packages/website/ts/components/eth_wrappers.tsx b/packages/website/ts/components/eth_wrappers.tsx
index a5758a66a..1db5ff77f 100644
--- a/packages/website/ts/components/eth_wrappers.tsx
+++ b/packages/website/ts/components/eth_wrappers.tsx
@@ -33,7 +33,7 @@ interface EthWrappersProps {
dispatcher: Dispatcher;
tokenByAddress: TokenByAddress;
userAddress: string;
- userEtherBalanceInWei: BigNumber;
+ userEtherBalanceInWei?: BigNumber;
lastForceTokenStateRefetch: number;
}
diff --git a/packages/website/ts/components/legacy_portal/legacy_portal.tsx b/packages/website/ts/components/legacy_portal/legacy_portal.tsx
index a5ea95629..e5d152e3e 100644
--- a/packages/website/ts/components/legacy_portal/legacy_portal.tsx
+++ b/packages/website/ts/components/legacy_portal/legacy_portal.tsx
@@ -38,6 +38,7 @@ import {
} from 'ts/types';
import { configs } from 'ts/utils/configs';
import { constants } from 'ts/utils/constants';
+import { orderParser } from 'ts/utils/order_parser';
import { Translate } from 'ts/utils/translate';
import { utils } from 'ts/utils/utils';
@@ -55,7 +56,7 @@ export interface LegacyPortalProps {
providerType: ProviderType;
screenWidth: ScreenWidths;
tokenByAddress: TokenByAddress;
- userEtherBalanceInWei: BigNumber;
+ userEtherBalanceInWei?: BigNumber;
userAddress: string;
shouldBlockchainErrDialogBeOpen: boolean;
userSuppliedOrderCache: Order;
@@ -86,7 +87,7 @@ export class LegacyPortal extends React.Component<LegacyPortalProps, LegacyPorta
}
constructor(props: LegacyPortalProps) {
super(props);
- this._sharedOrderIfExists = this._getSharedOrderIfExists();
+ this._sharedOrderIfExists = orderParser.parse(window.location.search);
this._throttledScreenWidthUpdate = _.throttle(this._updateScreenWidth.bind(this), THROTTLE_TIMEOUT);
const isViewingBalances = _.includes(props.location.pathname, `${WebsitePaths.Portal}/balances`);
@@ -362,32 +363,6 @@ export class LegacyPortal extends React.Component<LegacyPortalProps, LegacyPorta
isWethNoticeDialogOpen: false,
});
}
- private _getSharedOrderIfExists(): Order | undefined {
- const queryString = window.location.search;
- if (queryString.length === 0) {
- return undefined;
- }
- const queryParams = queryString.substring(1).split('&');
- const orderQueryParam = _.find(queryParams, queryParam => {
- const queryPair = queryParam.split('=');
- return queryPair[0] === 'order';
- });
- if (_.isUndefined(orderQueryParam)) {
- return undefined;
- }
- const orderPair = orderQueryParam.split('=');
- if (orderPair.length !== 2) {
- return undefined;
- }
-
- const order = JSON.parse(decodeURIComponent(orderPair[1]));
- const validationResult = validator.validate(order, portalOrderSchema);
- if (validationResult.errors.length > 0) {
- logUtils.log(`Invalid shared order: ${validationResult.errors}`);
- return undefined;
- }
- return order;
- }
private _updateScreenWidth(): void {
const newScreenWidth = utils.getScreenWidth();
this.props.dispatcher.updateScreenWidth(newScreenWidth);
diff --git a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx
index edaeb3736..2014dd7b0 100644
--- a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx
+++ b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx
@@ -14,7 +14,7 @@ export interface PortalOnboardingFlowProps {
providerType: ProviderType;
injectedProviderName: string;
blockchainIsLoaded: boolean;
- userEthBalanceInWei: BigNumber;
+ userEtherBalanceInWei?: BigNumber;
tokenByAddress: TokenByAddress;
updateIsRunning: (isRunning: boolean) => void;
updateOnboardingStep: (stepIndex: number) => void;
@@ -85,7 +85,7 @@ export class PortalOnboardingFlow extends React.Component<PortalOnboardingFlowPr
}
private _userHasEth(): boolean {
- return this.props.userEthBalanceInWei > new BigNumber(0);
+ return this.props.userEtherBalanceInWei > new BigNumber(0);
}
private _userHasWeth(): boolean {
diff --git a/packages/website/ts/components/portal/menu.tsx b/packages/website/ts/components/portal/menu.tsx
index 6a3301549..6e97ee37e 100644
--- a/packages/website/ts/components/portal/menu.tsx
+++ b/packages/website/ts/components/portal/menu.tsx
@@ -43,9 +43,14 @@ export const defaultMenuItemEntries: MenuItemEntry[] = [
iconName: 'zmdi-circle-o',
},
{
- to: `${WebsitePaths.Portal}/direct`,
- labelText: 'Trade direct',
- iconName: 'zmdi-swap',
+ to: `${WebsitePaths.Portal}/generate`,
+ labelText: 'Generate order',
+ iconName: 'zmdi-arrow-right-top',
+ },
+ {
+ to: `${WebsitePaths.Portal}/fill`,
+ labelText: 'Fill order',
+ iconName: 'zmdi-arrow-left-bottom',
},
];
diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx
index 0e1506e17..589ad00ad 100644
--- a/packages/website/ts/components/portal/portal.tsx
+++ b/packages/website/ts/components/portal/portal.tsx
@@ -10,6 +10,7 @@ import { BlockchainErrDialog } from 'ts/components/dialogs/blockchain_err_dialog
import { LedgerConfigDialog } from 'ts/components/dialogs/ledger_config_dialog';
import { PortalDisclaimerDialog } from 'ts/components/dialogs/portal_disclaimer_dialog';
import { EthWrappers } from 'ts/components/eth_wrappers';
+import { FillOrder } from 'ts/components/fill_order';
import { AssetPicker } from 'ts/components/generate_order/asset_picker';
import { BackButton } from 'ts/components/portal/back_button';
import { Loading } from 'ts/components/portal/loading';
@@ -42,6 +43,7 @@ import {
} from 'ts/types';
import { configs } from 'ts/utils/configs';
import { constants } from 'ts/utils/constants';
+import { orderParser } from 'ts/utils/order_parser';
import { Translate } from 'ts/utils/translate';
import { utils } from 'ts/utils/utils';
@@ -57,7 +59,7 @@ export interface PortalProps {
providerType: ProviderType;
screenWidth: ScreenWidths;
tokenByAddress: TokenByAddress;
- userEtherBalanceInWei: BigNumber;
+ userEtherBalanceInWei?: BigNumber;
userAddress: string;
shouldBlockchainErrDialogBeOpen: boolean;
userSuppliedOrderCache: Order;
@@ -116,9 +118,11 @@ const styles: Styles = {
export class Portal extends React.Component<PortalProps, PortalState> {
private _blockchain: Blockchain;
+ private _sharedOrderIfExists: Order;
private _throttledScreenWidthUpdate: () => void;
constructor(props: PortalProps) {
super(props);
+ this._sharedOrderIfExists = orderParser.parse(window.location.search);
this._throttledScreenWidthUpdate = _.throttle(this._updateScreenWidth.bind(this), THROTTLE_TIMEOUT);
const didAcceptPortalDisclaimer = localStorage.getItemIfExists(constants.LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER);
const hasAcceptedDisclaimer =
@@ -336,9 +340,14 @@ export class Portal extends React.Component<PortalProps, PortalState> {
render: this._renderTradeHistory.bind(this),
},
{
- pathName: `${WebsitePaths.Portal}/direct`,
- headerText: 'Trade Direct',
- render: this._renderTradeDirect.bind(this),
+ pathName: `${WebsitePaths.Portal}/generate`,
+ headerText: 'Generate Order',
+ render: this._renderGenerateOrderForm.bind(this),
+ },
+ {
+ pathName: `${WebsitePaths.Portal}/fill`,
+ headerText: 'Fill Order',
+ render: this._renderFillOrder.bind(this),
},
];
return (
@@ -386,7 +395,7 @@ export class Portal extends React.Component<PortalProps, PortalState> {
/>
);
}
- private _renderTradeDirect(match: any, location: Location, history: History): React.ReactNode {
+ private _renderGenerateOrderForm(): React.ReactNode {
return (
<GenerateOrderForm
blockchain={this._blockchain}
@@ -395,6 +404,25 @@ export class Portal extends React.Component<PortalProps, PortalState> {
/>
);
}
+ private _renderFillOrder(): React.ReactNode {
+ const initialFillOrder = !_.isUndefined(this.props.userSuppliedOrderCache)
+ ? this.props.userSuppliedOrderCache
+ : this._sharedOrderIfExists;
+ return (
+ <FillOrder
+ blockchain={this._blockchain}
+ blockchainErr={this.props.blockchainErr}
+ initialOrder={initialFillOrder}
+ isOrderInUrl={!_.isUndefined(this._sharedOrderIfExists)}
+ orderFillAmount={this.props.orderFillAmount}
+ networkId={this.props.networkId}
+ userAddress={this.props.userAddress}
+ tokenByAddress={this.props.tokenByAddress}
+ dispatcher={this.props.dispatcher}
+ lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch}
+ />
+ );
+ }
private _renderTokenBalances(): React.ReactNode {
const allTokens = _.values(this.props.tokenByAddress);
const trackedTokens = _.filter(allTokens, t => t.isTracked);
diff --git a/packages/website/ts/components/token_balances.tsx b/packages/website/ts/components/token_balances.tsx
index f5a51dabb..7a0742bbe 100644
--- a/packages/website/ts/components/token_balances.tsx
+++ b/packages/website/ts/components/token_balances.tsx
@@ -85,6 +85,9 @@ interface TokenBalancesState {
}
export class TokenBalances extends React.Component<TokenBalancesProps, TokenBalancesState> {
+ public static defaultProps: Partial<TokenBalancesProps> = {
+ userEtherBalanceInWei: new BigNumber(0),
+ };
private _isUnmounted: boolean;
public constructor(props: TokenBalancesProps) {
super(props);
diff --git a/packages/website/ts/components/top_bar/provider_display.tsx b/packages/website/ts/components/top_bar/provider_display.tsx
index 679ec07dc..8a337119a 100644
--- a/packages/website/ts/components/top_bar/provider_display.tsx
+++ b/packages/website/ts/components/top_bar/provider_display.tsx
@@ -1,5 +1,6 @@
import { Styles } from '@0xproject/react-shared';
import * as _ from 'lodash';
+import CircularProgress from 'material-ui/CircularProgress';
import RaisedButton from 'material-ui/RaisedButton';
import * as React from 'react';
@@ -23,7 +24,8 @@ export interface ProviderDisplayProps {
injectedProviderName: string;
providerType: ProviderType;
onToggleLedgerDialog: () => void;
- blockchain: Blockchain;
+ blockchain?: Blockchain;
+ blockchainIsLoaded: boolean;
}
interface ProviderDisplayState {}
@@ -44,11 +46,18 @@ export class ProviderDisplay extends React.Component<ProviderDisplayProps, Provi
this.props.providerType,
this.props.injectedProviderName,
);
- const displayAddress = isAddressAvailable
- ? utils.getAddressBeginAndEnd(this.props.userAddress)
- : isExternallyInjectedProvider
- ? 'Account locked'
- : '0x0000...0000';
+ let displayMessage;
+ if (!this._isBlockchainReady()) {
+ displayMessage = 'loading account';
+ } else if (isAddressAvailable) {
+ displayMessage = utils.getAddressBeginAndEnd(this.props.userAddress);
+ // tslint:disable-next-line: prefer-conditional-expression
+ } else if (isExternallyInjectedProvider) {
+ displayMessage = 'Account locked';
+ } else {
+ displayMessage = '0x0000...0000';
+ }
+
// If the "injected" provider is our fallback public node, then we want to
// show the "connect a wallet" message instead of the providerName
const injectedProviderName = isExternallyInjectedProvider
@@ -60,10 +69,14 @@ export class ProviderDisplay extends React.Component<ProviderDisplayProps, Provi
const hoverActiveNode = (
<div className="flex right lg-pr0 md-pr2 sm-pr2 p1" style={styles.root}>
<div>
- <Identicon address={this.props.userAddress} diameter={ROOT_HEIGHT} />
+ {this._isBlockchainReady() ? (
+ <Identicon address={this.props.userAddress} diameter={ROOT_HEIGHT} />
+ ) : (
+ <CircularProgress size={ROOT_HEIGHT} thickness={2} />
+ )}
</div>
<div style={{ marginLeft: 12, paddingTop: 3 }}>
- <div style={{ fontSize: 16, color: colors.darkGrey }}>{displayAddress}</div>
+ <div style={{ fontSize: 16, color: colors.darkGrey }}>{displayMessage}</div>
</div>
{isProviderMetamask && (
<div style={{ marginLeft: 16 }}>
@@ -87,7 +100,9 @@ export class ProviderDisplay extends React.Component<ProviderDisplayProps, Provi
);
}
public renderPopoverContent(hasInjectedProvider: boolean, hasLedgerProvider: boolean): React.ReactNode {
- if (hasInjectedProvider || hasLedgerProvider) {
+ if (!this._isBlockchainReady()) {
+ return null;
+ } else if (hasInjectedProvider || hasLedgerProvider) {
return (
<ProviderPicker
dispatcher={this.props.dispatcher}
@@ -159,4 +174,7 @@ export class ProviderDisplay extends React.Component<ProviderDisplayProps, Provi
);
}
}
+ private _isBlockchainReady(): boolean {
+ return this.props.blockchainIsLoaded && !_.isUndefined(this.props.blockchain);
+ }
}
diff --git a/packages/website/ts/components/top_bar/top_bar.tsx b/packages/website/ts/components/top_bar/top_bar.tsx
index db8e3cb82..e2d791ae3 100644
--- a/packages/website/ts/components/top_bar/top_bar.tsx
+++ b/packages/website/ts/components/top_bar/top_bar.tsx
@@ -261,7 +261,7 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> {
</div>
</div>
)}
- {this.props.blockchainIsLoaded && (
+ {this._isViewingPortal() && (
<div className="sm-hide xs-hide col col-5" style={{ paddingTop: 8, marginRight: 36 }}>
<ProviderDisplay
dispatcher={this.props.dispatcher}
@@ -271,6 +271,7 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> {
providerType={this.props.providerType}
onToggleLedgerDialog={this.props.onToggleLedgerDialog}
blockchain={this.props.blockchain}
+ blockchainIsLoaded={this.props.blockchainIsLoaded}
/>
</div>
)}
diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx
index 30d1285f4..18dada22f 100644
--- a/packages/website/ts/components/wallet/wallet.tsx
+++ b/packages/website/ts/components/wallet/wallet.tsx
@@ -7,6 +7,7 @@ import {
import { BigNumber } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as _ from 'lodash';
+import CircularProgress from 'material-ui/CircularProgress';
import FlatButton from 'material-ui/FlatButton';
import FloatingActionButton from 'material-ui/FloatingActionButton';
import { ListItem } from 'material-ui/List';
@@ -23,6 +24,7 @@ import firstBy = require('thenby');
import { Blockchain } from 'ts/blockchain';
import { AllowanceToggle } from 'ts/components/inputs/allowance_toggle';
+import { Container } from 'ts/components/ui/container';
import { IconButton } from 'ts/components/ui/icon_button';
import { Identicon } from 'ts/components/ui/identicon';
import { Island } from 'ts/components/ui/island';
@@ -59,7 +61,7 @@ export interface WalletProps {
dispatcher: Dispatcher;
tokenByAddress: TokenByAddress;
trackedTokens: Token[];
- userEtherBalanceInWei: BigNumber;
+ userEtherBalanceInWei?: BigNumber;
lastForceTokenStateRefetch: number;
injectedProviderName: string;
providerType: ProviderType;
@@ -92,9 +94,6 @@ const styles: Styles = {
zIndex: zIndex.aboveOverlay,
position: 'relative',
},
- headerItemInnerDiv: {
- paddingLeft: 65,
- },
footerItemInnerDiv: {
paddingLeft: 24,
borderTopColor: colors.walletBorder,
@@ -108,6 +107,7 @@ const styles: Styles = {
},
tokenItem: {
backgroundColor: colors.walletDefaultItemBackground,
+ minHeight: 85,
},
amountLabel: {
fontWeight: 'bold',
@@ -129,10 +129,13 @@ const styles: Styles = {
color: colors.mediumBlue,
fontWeight: 'bold',
},
+ loadingBody: {
+ height: 381,
+ },
};
const ETHER_ICON_PATH = '/images/ether.png';
-const ICON_DIMENSION = 24;
+const ICON_DIMENSION = 28;
const TOKEN_AMOUNT_DISPLAY_PRECISION = 3;
const BODY_ITEM_KEY = 'BODY';
const HEADER_ITEM_KEY = 'HEADER';
@@ -191,26 +194,40 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
}
}
public render(): React.ReactNode {
- const isReadyToRender = this.props.blockchainIsLoaded && this.props.blockchainErr === BlockchainErrs.NoError;
- const isAddressAvailable = !_.isEmpty(this.props.userAddress);
+ const isBlockchainLoaded = this.props.blockchainIsLoaded && this.props.blockchainErr === BlockchainErrs.NoError;
return (
<Island className="flex flex-column wallet" style={styles.root}>
- {isReadyToRender && isAddressAvailable
- ? _.concat(this._renderConnectedHeaderRows(), this._renderBody(), this._renderFooterRows())
- : _.concat(this._renderDisconnectedHeaderRows(), this._renderDisconnectedRows())}
+ {isBlockchainLoaded ? this._renderLoadedRows() : this._renderLoadingRows()}
</Island>
);
}
+ private _renderLoadedRows(): React.ReactNode {
+ const isAddressAvailable = !_.isEmpty(this.props.userAddress);
+ return isAddressAvailable
+ ? _.concat(this._renderConnectedHeaderRows(), this._renderBody(), this._renderFooterRows())
+ : _.concat(this._renderDisconnectedHeaderRows(), this._renderDisconnectedRows());
+ }
+ private _renderLoadingRows(): React.ReactNode {
+ return _.concat(this._renderDisconnectedHeaderRows(), this._renderLoadingBodyRows());
+ }
+ private _renderLoadingBodyRows(): React.ReactElement<{}> {
+ return (
+ <div key={BODY_ITEM_KEY} className="flex items-center" style={styles.loadingBody}>
+ <div className="mx-auto">
+ <CircularProgress size={40} thickness={5} />
+ </div>
+ </div>
+ );
+ }
private _renderDisconnectedHeaderRows(): React.ReactElement<{}> {
const userAddress = this.props.userAddress;
const primaryText = 'wallet';
return (
- <ListItem
+ <StandardIconRow
key={HEADER_ITEM_KEY}
- primaryText={primaryText.toUpperCase()}
- leftIcon={<ActionAccountBalanceWallet color={colors.mediumBlue} />}
- style={styles.paddedItem}
- innerDivStyle={styles.headerItemInnerDiv}
+ icon={<ActionAccountBalanceWallet color={colors.mediumBlue} />}
+ main={primaryText.toUpperCase()}
+ style={styles.borderedItem}
/>
);
}
@@ -229,11 +246,10 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
const primaryText = utils.getAddressBeginAndEnd(userAddress);
return (
<Link key={HEADER_ITEM_KEY} to={ACCOUNT_PATH} style={{ textDecoration: 'none' }}>
- <ListItem
- primaryText={primaryText}
- leftIcon={<Identicon address={userAddress} diameter={ICON_DIMENSION} />}
- style={{ ...styles.paddedItem, ...styles.borderedItem }}
- innerDivStyle={styles.headerItemInnerDiv}
+ <StandardIconRow
+ icon={<Identicon address={userAddress} diameter={ICON_DIMENSION} />}
+ main={primaryText}
+ style={styles.borderedItem}
/>
</Link>
);
@@ -320,26 +336,23 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
private _renderEthRows(): React.ReactNode {
const icon = <img style={{ width: ICON_DIMENSION, height: ICON_DIMENSION }} src={ETHER_ICON_PATH} />;
const primaryText = this._renderAmount(
- this.props.userEtherBalanceInWei,
+ this.props.userEtherBalanceInWei || new BigNumber(0),
constants.DECIMAL_PLACES_ETH,
constants.ETHER_SYMBOL,
+ _.isUndefined(this.props.userEtherBalanceInWei),
);
const etherToken = this._getEthToken();
- const etherPrice = this.state.trackedTokenStateByAddress[etherToken.address].price;
+ const etherTokenState = this.state.trackedTokenStateByAddress[etherToken.address];
+ const etherPrice = etherTokenState.price;
const secondaryText = this._renderValue(
- this.props.userEtherBalanceInWei,
+ this.props.userEtherBalanceInWei || new BigNumber(0),
constants.DECIMAL_PLACES_ETH,
etherPrice,
+ _.isUndefined(this.props.userEtherBalanceInWei) || !etherTokenState.isLoaded,
);
const accessoryItemConfig = {
wrappedEtherDirection: Side.Deposit,
};
- const isInWrappedEtherState =
- !_.isUndefined(this.state.wrappedEtherDirection) &&
- this.state.wrappedEtherDirection === accessoryItemConfig.wrappedEtherDirection;
- const style = isInWrappedEtherState
- ? { ...walletItemStyles.focusedItem, ...styles.paddedItem }
- : { ...styles.tokenItem, ...styles.borderedItem, ...styles.paddedItem };
const key = ETHER_ITEM_KEY;
return this._renderBalanceRow(key, icon, primaryText, secondaryText, accessoryItemConfig, 'eth-row');
}
@@ -360,10 +373,15 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
EtherscanLinkSuffixes.Address,
);
const icon = <TokenIcon token={token} diameter={ICON_DIMENSION} link={tokenLink} />;
- const primaryText = this._renderAmount(tokenState.balance, token.decimals, token.symbol);
- const secondaryText = this._renderValue(tokenState.balance, token.decimals, tokenState.price);
const isWeth = token.symbol === constants.ETHER_TOKEN_SYMBOL;
const wrappedEtherDirection = isWeth ? Side.Receive : undefined;
+ const primaryText = this._renderAmount(tokenState.balance, token.decimals, token.symbol, !tokenState.isLoaded);
+ const secondaryText = this._renderValue(
+ tokenState.balance,
+ token.decimals,
+ tokenState.price,
+ !tokenState.isLoaded,
+ );
const accessoryItemConfig: AccessoryItemConfig = {
wrappedEtherDirection,
allowanceToggleConfig: {
@@ -391,22 +409,24 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
): React.ReactNode {
const shouldShowWrapEtherItem =
!_.isUndefined(this.state.wrappedEtherDirection) &&
- this.state.wrappedEtherDirection === accessoryItemConfig.wrappedEtherDirection;
- const style = shouldShowWrapEtherItem
- ? { ...walletItemStyles.focusedItem, ...styles.paddedItem }
- : { ...styles.tokenItem, ...styles.borderedItem, ...styles.paddedItem };
+ this.state.wrappedEtherDirection === accessoryItemConfig.wrappedEtherDirection &&
+ !_.isUndefined(this.props.userEtherBalanceInWei);
+ const additionalStyle = shouldShowWrapEtherItem ? walletItemStyles.focusedItem : styles.borderedItem;
+ const style = { ...styles.tokenItem, ...additionalStyle };
const etherToken = this._getEthToken();
return (
<div key={key} className={`flex flex-column ${className || ''}`}>
- <div className="flex items-center" style={style}>
- <div className="px2">{icon}</div>
- <div className="flex-none pr2 pt2 pb2">
- {primaryText}
- {secondaryText}
- </div>
- <div className="flex-auto" />
- <div>{this._renderAccessoryItems(accessoryItemConfig)}</div>
- </div>
+ <StandardIconRow
+ icon={icon}
+ main={
+ <div className="flex flex-column">
+ {primaryText}
+ <Container marginTop="3px">{secondaryText}</Container>
+ </div>
+ }
+ accessory={this._renderAccessoryItems(accessoryItemConfig)}
+ style={style}
+ />
{shouldShowWrapEtherItem && (
<WrapEtherItem
userAddress={this.props.userAddress}
@@ -458,21 +478,45 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
/>
);
}
- private _renderAmount(amount: BigNumber, decimals: number, symbol: string): React.ReactNode {
+ private _renderAmount(
+ amount: BigNumber,
+ decimals: number,
+ symbol: string,
+ isLoading: boolean = false,
+ ): React.ReactNode {
const unitAmount = Web3Wrapper.toUnitAmount(amount, decimals);
const formattedAmount = unitAmount.toPrecision(TOKEN_AMOUNT_DISPLAY_PRECISION);
const result = `${formattedAmount} ${symbol}`;
- return <div style={styles.amountLabel}>{result}</div>;
+ return (
+ <PlaceHolder hideChildren={isLoading}>
+ <div style={styles.amountLabel}>{result}</div>
+ </PlaceHolder>
+ );
}
- private _renderValue(amount: BigNumber, decimals: number, price?: BigNumber): React.ReactNode {
- if (_.isUndefined(price)) {
- return null;
+ private _renderValue(
+ amount: BigNumber,
+ decimals: number,
+ price?: BigNumber,
+ isLoading: boolean = false,
+ ): React.ReactNode {
+ let result;
+ if (!isLoading) {
+ if (_.isUndefined(price)) {
+ result = '--';
+ } else {
+ const unitAmount = Web3Wrapper.toUnitAmount(amount, decimals);
+ const value = unitAmount.mul(price);
+ const formattedAmount = value.toFixed(USD_DECIMAL_PLACES);
+ result = `$${formattedAmount}`;
+ }
+ } else {
+ result = '$0.00';
}
- const unitAmount = Web3Wrapper.toUnitAmount(amount, decimals);
- const value = unitAmount.mul(price);
- const formattedAmount = value.toFixed(USD_DECIMAL_PLACES);
- const result = `$${formattedAmount}`;
- return <div style={styles.valueLabel}>{result}</div>;
+ return (
+ <PlaceHolder hideChildren={isLoading}>
+ <div style={styles.valueLabel}>{result}</div>
+ </PlaceHolder>
+ );
}
private _renderWrappedEtherButton(wrappedEtherDirection: Side): React.ReactNode {
const isWrappedEtherDirectionOpen = this.state.wrappedEtherDirection === wrappedEtherDirection;
@@ -589,4 +633,41 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
private _getEthToken(): Token {
return utils.getEthToken(this.props.tokenByAddress);
}
-} // tslint:disable:max-file-line-count
+}
+
+interface StandardIconRowProps {
+ icon: React.ReactNode;
+ main: React.ReactNode;
+ accessory?: React.ReactNode;
+ style?: React.CSSProperties;
+}
+const StandardIconRow = (props: StandardIconRowProps) => {
+ return (
+ <div className="flex items-center" style={props.style}>
+ <div className="p2">{props.icon}</div>
+ <div className="flex-none pr2 pt2 pb2">{props.main}</div>
+ <div className="flex-auto" />
+ <div>{props.accessory}</div>
+ </div>
+ );
+};
+interface PlaceHolderProps {
+ hideChildren: React.ReactNode;
+ children?: React.ReactNode;
+}
+const PlaceHolder = (props: PlaceHolderProps) => {
+ const rootBackgroundColor = props.hideChildren ? colors.lightGrey : 'transparent';
+ const rootStyle: React.CSSProperties = {
+ backgroundColor: rootBackgroundColor,
+ display: 'inline-block',
+ borderRadius: 2,
+ };
+ const childrenVisibility = props.hideChildren ? 'hidden' : 'visible';
+ const childrenStyle: React.CSSProperties = { visibility: childrenVisibility };
+ return (
+ <div style={rootStyle}>
+ <div style={childrenStyle}>{props.children}</div>
+ </div>
+ );
+};
+// tslint:disable:max-file-line-count
diff --git a/packages/website/ts/components/wallet/wallet_disconnected_item.tsx b/packages/website/ts/components/wallet/wallet_disconnected_item.tsx
index 39a62e1fb..17fd8a19e 100644
--- a/packages/website/ts/components/wallet/wallet_disconnected_item.tsx
+++ b/packages/website/ts/components/wallet/wallet_disconnected_item.tsx
@@ -31,7 +31,7 @@ const styles: Styles = {
},
};
-const ITEM_HEIGHT = 292;
+const ITEM_HEIGHT = 381;
const METAMASK_ICON_WIDTH = 35;
const LEDGER_ICON_WIDTH = 30;
const BUTTON_BOTTOM_PADDING = 80;
diff --git a/packages/website/ts/containers/legacy_portal.ts b/packages/website/ts/containers/legacy_portal.ts
index 3b1172a44..eae450c21 100644
--- a/packages/website/ts/containers/legacy_portal.ts
+++ b/packages/website/ts/containers/legacy_portal.ts
@@ -24,7 +24,7 @@ interface ConnectedState {
providerType: ProviderType;
tokenByAddress: TokenByAddress;
lastForceTokenStateRefetch: number;
- userEtherBalanceInWei: BigNumber;
+ userEtherBalanceInWei?: BigNumber;
screenWidth: ScreenWidths;
shouldBlockchainErrDialogBeOpen: boolean;
userAddress: string;
diff --git a/packages/website/ts/containers/portal.ts b/packages/website/ts/containers/portal.ts
index 3f0feb6e9..b8c8fb999 100644
--- a/packages/website/ts/containers/portal.ts
+++ b/packages/website/ts/containers/portal.ts
@@ -21,7 +21,7 @@ interface ConnectedState {
providerType: ProviderType;
tokenByAddress: TokenByAddress;
lastForceTokenStateRefetch: number;
- userEtherBalanceInWei: BigNumber;
+ userEtherBalanceInWei?: BigNumber;
screenWidth: ScreenWidths;
shouldBlockchainErrDialogBeOpen: boolean;
userAddress: string;
diff --git a/packages/website/ts/containers/portal_onboarding_flow.ts b/packages/website/ts/containers/portal_onboarding_flow.ts
index 84739192f..8202fb2ae 100644
--- a/packages/website/ts/containers/portal_onboarding_flow.ts
+++ b/packages/website/ts/containers/portal_onboarding_flow.ts
@@ -17,7 +17,7 @@ interface ConnectedState {
providerType: ProviderType;
injectedProviderName: string;
blockchainIsLoaded: boolean;
- userEthBalanceInWei: BigNumber;
+ userEtherBalanceInWei?: BigNumber;
tokenByAddress: TokenByAddress;
}
@@ -33,7 +33,7 @@ const mapStateToProps = (state: State): ConnectedState => ({
providerType: state.providerType,
injectedProviderName: state.injectedProviderName,
blockchainIsLoaded: state.blockchainIsLoaded,
- userEthBalanceInWei: state.userEtherBalanceInWei,
+ userEtherBalanceInWei: state.userEtherBalanceInWei,
tokenByAddress: state.tokenByAddress,
hasBeenSeen: state.hasPortalOnboardingBeenSeen,
});
diff --git a/packages/website/ts/containers/sol_cov_documentation.ts b/packages/website/ts/containers/sol_cov_documentation.ts
index 58755c1e0..bc05b6854 100644
--- a/packages/website/ts/containers/sol_cov_documentation.ts
+++ b/packages/website/ts/containers/sol_cov_documentation.ts
@@ -22,6 +22,9 @@ const docSections = {
installation: 'installation',
usage: 'usage',
coverageSubprovider: 'coverageSubprovider',
+ abstractArtifactAdapter: 'abstractArtifactAdapter',
+ solCompilerArtifactAdapter: 'solCompilerArtifactAdapter',
+ truffleArtifactAdapter: 'truffleArtifactAdapter',
types: docConstants.TYPES_SECTION_NAME,
};
@@ -34,7 +37,10 @@ const docsInfoConfig: DocsInfoConfig = {
introduction: [docSections.introduction],
install: [docSections.installation],
usage: [docSections.usage],
- coverageSubprovider: [docSections.coverageSubprovider],
+ 'coverage-subprovider': [docSections.coverageSubprovider],
+ 'abstract-artifact-adapter': [docSections.abstractArtifactAdapter],
+ 'sol-compiler-artifact-adapter': [docSections.solCompilerArtifactAdapter],
+ 'truffle-artifact-adapter': [docSections.truffleArtifactAdapter],
types: [docSections.types],
},
sectionNameToMarkdown: {
@@ -44,18 +50,40 @@ const docsInfoConfig: DocsInfoConfig = {
},
sectionNameToModulePath: {
[docSections.coverageSubprovider]: ['"sol-cov/src/coverage_subprovider"'],
+ [docSections.abstractArtifactAdapter]: ['"sol-cov/src/artifact_adapters/abstract_artifact_adapter"'],
+ [docSections.solCompilerArtifactAdapter]: ['"sol-cov/src/artifact_adapters/sol_compiler_artifact_adapter"'],
+ [docSections.truffleArtifactAdapter]: ['"sol-cov/src/artifact_adapters/truffle_artifact_adapter"'],
[docSections.types]: ['"subproviders/src/types"', '"types/src/index"'],
},
menuSubsectionToVersionWhenIntroduced: {},
sections: docSections,
- visibleConstructors: [docSections.coverageSubprovider],
+ visibleConstructors: [
+ docSections.coverageSubprovider,
+ docSections.abstractArtifactAdapter,
+ docSections.solCompilerArtifactAdapter,
+ docSections.truffleArtifactAdapter,
+ ],
typeConfigs: {
// Note: This needs to be kept in sync with the types exported in index.ts. Unfortunately there is
// currently no way to extract the re-exported types from index.ts via TypeDoc :(
- publicTypes: ['NextCallback', 'OnNextCompleted', 'ErrorCallback', 'JSONRPCRequestPayload'],
+ publicTypes: [
+ 'JSONRPCRequestPayload',
+ 'NextCallback',
+ 'ErrorCallback',
+ 'AbstractArtifactAdapter',
+ 'CoverageSubprovider',
+ 'TruffleArtifactAdapter',
+ 'SolCompilerArtifactAdapter',
+ 'ContractData',
+ ],
typeNameToExternalLink: {},
typeNameToPrefix: {},
- typeNameToDocSection: {},
+ typeNameToDocSection: {
+ AbstractArtifactAdapter: docSections.abstractArtifactAdapter,
+ CoverageSubprovider: docSections.coverageSubprovider,
+ TruffleArtifactAdapter: docSections.truffleArtifactAdapter,
+ SolCompilerArtifactAdapter: docSections.solCompilerArtifactAdapter,
+ },
},
};
const docsInfo = new DocsInfo(docsInfoConfig);
diff --git a/packages/website/ts/redux/dispatcher.ts b/packages/website/ts/redux/dispatcher.ts
index 0b4cc3938..e0ce43ae5 100644
--- a/packages/website/ts/redux/dispatcher.ts
+++ b/packages/website/ts/redux/dispatcher.ts
@@ -155,7 +155,7 @@ export class Dispatcher {
type: ActionTypes.UpdateOrderECSignature,
});
}
- public updateUserWeiBalance(balance: BigNumber): void {
+ public updateUserWeiBalance(balance?: BigNumber): void {
this._dispatch({
data: balance,
type: ActionTypes.UpdateUserEtherBalance,
diff --git a/packages/website/ts/redux/reducer.ts b/packages/website/ts/redux/reducer.ts
index 5c57792f7..9d3d8f7d9 100644
--- a/packages/website/ts/redux/reducer.ts
+++ b/packages/website/ts/redux/reducer.ts
@@ -39,7 +39,7 @@ export interface State {
tokenByAddress: TokenByAddress;
lastForceTokenStateRefetch: number;
userAddress: string;
- userEtherBalanceInWei: BigNumber;
+ userEtherBalanceInWei?: BigNumber;
portalOnboardingStep: number;
isPortalOnboardingShowing: boolean;
hasPortalOnboardingBeenSeen: boolean;
@@ -81,7 +81,7 @@ export const INITIAL_STATE: State = {
tokenByAddress: {},
lastForceTokenStateRefetch: moment().unix(),
userAddress: '',
- userEtherBalanceInWei: new BigNumber(0),
+ userEtherBalanceInWei: undefined,
userSuppliedOrderCache: undefined,
portalOnboardingStep: 0,
isPortalOnboardingShowing: false,
diff --git a/packages/website/ts/utils/order_parser.ts b/packages/website/ts/utils/order_parser.ts
new file mode 100644
index 000000000..be08da80e
--- /dev/null
+++ b/packages/website/ts/utils/order_parser.ts
@@ -0,0 +1,33 @@
+import { logUtils } from '@0xproject/utils';
+import * as _ from 'lodash';
+
+import { portalOrderSchema } from 'ts/schemas/portal_order_schema';
+import { validator } from 'ts/schemas/validator';
+import { Order } from 'ts/types';
+
+export const orderParser = {
+ parse(queryString: string): Order | undefined {
+ if (queryString.length === 0) {
+ return undefined;
+ }
+ const queryParams = queryString.substring(1).split('&');
+ const orderQueryParam = _.find(queryParams, queryParam => {
+ const queryPair = queryParam.split('=');
+ return queryPair[0] === 'order';
+ });
+ if (_.isUndefined(orderQueryParam)) {
+ return undefined;
+ }
+ const orderPair = orderQueryParam.split('=');
+ if (orderPair.length !== 2) {
+ return undefined;
+ }
+ const order = JSON.parse(decodeURIComponent(orderPair[1]));
+ const validationResult = validator.validate(order, portalOrderSchema);
+ if (validationResult.errors.length > 0) {
+ logUtils.log(`Invalid shared order: ${validationResult.errors}`);
+ return undefined;
+ }
+ return order;
+ },
+};