From c6dece6bd1e5f5afa56b290557eb7a6245c76cb6 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 30 Jan 2018 13:26:46 +0100 Subject: Add config file specifically in prettier command and fix files --- packages/website/ts/blockchain.ts | 1504 ++++++++++---------- .../components/dialogs/blockchain_err_dialog.tsx | 282 ++-- .../dialogs/eth_weth_conversion_dialog.tsx | 290 ++-- .../ts/components/dialogs/ledger_config_dialog.tsx | 460 +++--- .../dialogs/portal_disclaimer_dialog.tsx | 52 +- .../website/ts/components/dialogs/send_dialog.tsx | 200 +-- .../dialogs/track_token_confirmation_dialog.tsx | 160 +-- .../dialogs/u2f_not_supported_dialog.tsx | 70 +- .../dialogs/wrapped_eth_section_notice_dialog.tsx | 46 +- .../ts/components/eth_weth_conversion_button.tsx | 208 +-- packages/website/ts/components/eth_wrappers.tsx | 674 ++++----- packages/website/ts/components/fill_order.tsx | 1270 ++++++++--------- packages/website/ts/components/fill_order_json.tsx | 124 +- .../website/ts/components/fill_warning_dialog.tsx | 70 +- .../flash_messages/token_send_completed.tsx | 38 +- .../flash_messages/transaction_submitted.tsx | 30 +- packages/website/ts/components/footer.tsx | 362 ++--- .../ts/components/generate_order/asset_picker.tsx | 498 +++---- .../generate_order/generate_order_form.tsx | 620 ++++---- .../components/generate_order/new_token_form.tsx | 418 +++--- .../website/ts/components/inputs/address_input.tsx | 112 +- .../ts/components/inputs/allowance_toggle.tsx | 142 +- .../ts/components/inputs/balance_bounded_input.tsx | 264 ++-- .../ts/components/inputs/eth_amount_input.tsx | 68 +- .../ts/components/inputs/expiration_input.tsx | 168 +-- .../website/ts/components/inputs/hash_input.tsx | 86 +- .../components/inputs/identicon_address_input.tsx | 76 +- .../ts/components/inputs/token_amount_input.tsx | 108 +- .../website/ts/components/inputs/token_input.tsx | 164 +-- packages/website/ts/components/order_json.tsx | 322 ++--- packages/website/ts/components/portal.tsx | 630 ++++---- packages/website/ts/components/portal_menu.tsx | 122 +- packages/website/ts/components/send_button.tsx | 136 +- packages/website/ts/components/token_balances.tsx | 1066 +++++++------- packages/website/ts/components/top_bar.tsx | 634 ++++----- .../website/ts/components/top_bar_menu_item.tsx | 74 +- .../ts/components/track_token_confirmation.tsx | 94 +- .../ts/components/trade_history/trade_history.tsx | 190 +-- .../trade_history/trade_history_item.tsx | 282 ++-- packages/website/ts/components/ui/alert.tsx | 30 +- packages/website/ts/components/ui/badge.tsx | 84 +- packages/website/ts/components/ui/copy_icon.tsx | 126 +- .../ts/components/ui/drop_down_menu_item.tsx | 170 +-- .../website/ts/components/ui/ethereum_address.tsx | 38 +- .../website/ts/components/ui/etherscan_icon.tsx | 52 +- .../website/ts/components/ui/fake_text_field.tsx | 44 +- .../website/ts/components/ui/flash_message.tsx | 52 +- packages/website/ts/components/ui/help_tooltip.tsx | 28 +- packages/website/ts/components/ui/identicon.tsx | 72 +- packages/website/ts/components/ui/input_label.tsx | 28 +- .../ts/components/ui/lifecycle_raised_button.tsx | 166 +-- packages/website/ts/components/ui/loading.tsx | 52 +- packages/website/ts/components/ui/menu_item.tsx | 78 +- packages/website/ts/components/ui/party.tsx | 240 ++-- .../website/ts/components/ui/required_label.tsx | 14 +- .../website/ts/components/ui/simple_loading.tsx | 18 +- packages/website/ts/components/ui/swap_icon.tsx | 60 +- packages/website/ts/components/ui/token_icon.tsx | 30 +- packages/website/ts/components/visual_order.tsx | 120 +- .../ts/containers/connect_documentation.tsx | 116 +- .../website/ts/containers/generate_order_form.tsx | 64 +- packages/website/ts/containers/portal.tsx | 112 +- .../containers/smart_contracts_documentation.tsx | 58 +- .../ts/containers/zero_ex_js_documentation.tsx | 258 ++-- packages/website/ts/globals.d.ts | 170 +-- packages/website/ts/index.tsx | 60 +- packages/website/ts/lazy_component.tsx | 82 +- packages/website/ts/local_storage/local_storage.ts | 62 +- .../ts/local_storage/tracked_token_storage.ts | 114 +- .../ts/local_storage/trade_history_storage.tsx | 160 +-- packages/website/ts/pages/about/about.tsx | 416 +++--- packages/website/ts/pages/about/profile.tsx | 116 +- .../website/ts/pages/documentation/comment.tsx | 16 +- .../website/ts/pages/documentation/custom_enum.tsx | 34 +- .../website/ts/pages/documentation/docs_info.ts | 198 +-- .../ts/pages/documentation/documentation.tsx | 648 ++++----- packages/website/ts/pages/documentation/enum.tsx | 26 +- .../ts/pages/documentation/event_definition.tsx | 132 +- .../website/ts/pages/documentation/interface.tsx | 98 +- .../ts/pages/documentation/method_block.tsx | 238 ++-- .../ts/pages/documentation/method_signature.tsx | 148 +- .../website/ts/pages/documentation/source_link.tsx | 38 +- packages/website/ts/pages/documentation/type.tsx | 354 ++--- .../ts/pages/documentation/type_definition.tsx | 194 +-- packages/website/ts/pages/faq/faq.tsx | 846 +++++------ packages/website/ts/pages/faq/question.tsx | 76 +- packages/website/ts/pages/landing/landing.tsx | 1420 +++++++++--------- packages/website/ts/pages/not_found.tsx | 52 +- packages/website/ts/pages/shared/anchor_title.tsx | 142 +- .../ts/pages/shared/markdown_code_block.tsx | 28 +- .../website/ts/pages/shared/markdown_section.tsx | 112 +- .../ts/pages/shared/nested_sidebar_menu.tsx | 268 ++-- .../website/ts/pages/shared/section_header.tsx | 72 +- .../website/ts/pages/shared/version_drop_down.tsx | 50 +- packages/website/ts/pages/wiki/wiki.tsx | 344 ++--- packages/website/ts/redux/dispatcher.ts | 452 +++--- packages/website/ts/redux/reducer.ts | 738 +++++----- packages/website/ts/schemas/order_schema.ts | 26 +- packages/website/ts/schemas/order_taker_schema.ts | 18 +- .../website/ts/schemas/signature_data_schema.ts | 18 +- packages/website/ts/schemas/token_schema.ts | 18 +- packages/website/ts/schemas/validator.ts | 22 +- packages/website/ts/types.ts | 782 +++++----- packages/website/ts/utils/colors.ts | 78 +- packages/website/ts/utils/configs.ts | 230 +-- packages/website/ts/utils/constants.ts | 164 +-- packages/website/ts/utils/doc_utils.ts | 84 +- packages/website/ts/utils/doxity_utils.ts | 316 ++-- packages/website/ts/utils/error_reporter.ts | 74 +- packages/website/ts/utils/mui_theme.ts | 60 +- packages/website/ts/utils/typedoc_utils.ts | 678 ++++----- packages/website/ts/utils/utils.ts | 522 +++---- packages/website/ts/web3_wrapper.ts | 288 ++-- 113 files changed, 12753 insertions(+), 12753 deletions(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index 711c3329d..5530701c0 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -1,25 +1,25 @@ import { - BlockParam, - BlockRange, - DecodedLogEvent, - ExchangeContractEventArgs, - ExchangeEvents, - IndexedFilterValues, - LogCancelContractEventArgs, - LogFillContractEventArgs, - LogWithDecodedArgs, - Order, - SignedOrder, - Token as ZeroExToken, - TransactionReceiptWithDecodedLogs, - ZeroEx, + BlockParam, + BlockRange, + DecodedLogEvent, + ExchangeContractEventArgs, + ExchangeEvents, + IndexedFilterValues, + LogCancelContractEventArgs, + LogFillContractEventArgs, + LogWithDecodedArgs, + Order, + SignedOrder, + Token as ZeroExToken, + TransactionReceiptWithDecodedLogs, + ZeroEx, } from '0x.js'; import { - InjectedWeb3Subprovider, - ledgerEthereumBrowserClientFactoryAsync, - LedgerSubprovider, - LedgerWalletSubprovider, - RedundantRPCSubprovider, + InjectedWeb3Subprovider, + ledgerEthereumBrowserClientFactoryAsync, + LedgerSubprovider, + LedgerWalletSubprovider, + RedundantRPCSubprovider, } from '@0xproject/subproviders'; import { BigNumber, intervalUtils, promisify } from '@0xproject/utils'; import * as _ from 'lodash'; @@ -31,16 +31,16 @@ import { trackedTokenStorage } from 'ts/local_storage/tracked_token_storage'; import { tradeHistoryStorage } from 'ts/local_storage/trade_history_storage'; import { Dispatcher } from 'ts/redux/dispatcher'; import { - BlockchainCallErrs, - BlockchainErrs, - ContractInstance, - EtherscanLinkSuffixes, - ProviderType, - Side, - SignatureData, - Token, - TokenByAddress, - TokenStateByAddress, + BlockchainCallErrs, + BlockchainErrs, + ContractInstance, + EtherscanLinkSuffixes, + ProviderType, + Side, + SignatureData, + Token, + TokenByAddress, + TokenStateByAddress, } from 'ts/types'; import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; @@ -56,727 +56,727 @@ import * as MintableArtifacts from '../contracts/Mintable.json'; const BLOCK_NUMBER_BACK_TRACK = 50; export class Blockchain { - public networkId: number; - public nodeVersion: string; - private _zeroEx: ZeroEx; - private _dispatcher: Dispatcher; - private _web3Wrapper?: Web3Wrapper; - private _exchangeAddress: string; - private _userAddress: string; - private _cachedProvider: Web3.Provider; - private _ledgerSubprovider: LedgerWalletSubprovider; - private _zrxPollIntervalId: NodeJS.Timer; - private static async _onPageLoadAsync(): Promise { - if (document.readyState === 'complete') { - return; // Already loaded - } - return new Promise((resolve, reject) => { - window.onload = () => resolve(); - }); - } - private static _getNameGivenProvider(provider: Web3.Provider): string { - if (!_.isUndefined((provider as any).isMetaMask)) { - return constants.PROVIDER_NAME_METAMASK; - } - - // HACK: We use the fact that Parity Signer's provider is an instance of their - // internal `Web3FrameProvider` class. - const isParitySigner = _.startsWith(provider.constructor.toString(), 'function Web3FrameProvider'); - if (isParitySigner) { - return constants.PROVIDER_NAME_PARITY_SIGNER; - } - - return constants.PROVIDER_NAME_GENERIC; - } - private static async _getProviderAsync(injectedWeb3: Web3, networkIdIfExists: number) { - const doesInjectedWeb3Exist = !_.isUndefined(injectedWeb3); - const publicNodeUrlsIfExistsForNetworkId = configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkIdIfExists]; - const isPublicNodeAvailableForNetworkId = !_.isUndefined(publicNodeUrlsIfExistsForNetworkId); - - let provider; - if (doesInjectedWeb3Exist && isPublicNodeAvailableForNetworkId) { - // We catch all requests involving a users account and send it to the injectedWeb3 - // instance. All other requests go to the public hosted node. - provider = new ProviderEngine(); - provider.addProvider(new InjectedWeb3Subprovider(injectedWeb3)); - provider.addProvider(new FilterSubprovider()); - provider.addProvider(new RedundantRPCSubprovider(publicNodeUrlsIfExistsForNetworkId)); - provider.start(); - } else if (doesInjectedWeb3Exist) { - // Since no public node for this network, all requests go to injectedWeb3 instance - provider = injectedWeb3.currentProvider; - } else { - // If no injectedWeb3 instance, all requests fallback to our public hosted mainnet/testnet node - // We do this so that users can still browse the 0x Portal DApp even if they do not have web3 - // injected into their browser. - provider = new ProviderEngine(); - provider.addProvider(new FilterSubprovider()); - const networkId = configs.IS_MAINNET_ENABLED ? constants.NETWORK_ID_MAINNET : constants.NETWORK_ID_TESTNET; - provider.addProvider(new RedundantRPCSubprovider(configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkId])); - provider.start(); - } - - return provider; - } - constructor(dispatcher: Dispatcher, isSalePage: boolean = false) { - this._dispatcher = dispatcher; - this._userAddress = ''; - // tslint:disable-next-line:no-floating-promises - this._onPageLoadInitFireAndForgetAsync(); - } - public async networkIdUpdatedFireAndForgetAsync(newNetworkId: number) { - const isConnected = !_.isUndefined(newNetworkId); - if (!isConnected) { - this.networkId = newNetworkId; - this._dispatcher.encounteredBlockchainError(BlockchainErrs.DisconnectedFromEthereumNode); - this._dispatcher.updateShouldBlockchainErrDialogBeOpen(true); - } else if (this.networkId !== newNetworkId) { - this.networkId = newNetworkId; - this._dispatcher.encounteredBlockchainError(BlockchainErrs.NoError); - await this._fetchTokenInformationAsync(); - await this._rehydrateStoreWithContractEvents(); - } - } - public async userAddressUpdatedFireAndForgetAsync(newUserAddress: string) { - if (this._userAddress !== newUserAddress) { - this._userAddress = newUserAddress; - await this._fetchTokenInformationAsync(); - await this._rehydrateStoreWithContractEvents(); - } - } - public async nodeVersionUpdatedFireAndForgetAsync(nodeVersion: string) { - if (this.nodeVersion !== nodeVersion) { - this.nodeVersion = nodeVersion; - } - } - public async isAddressInTokenRegistryAsync(tokenAddress: string): Promise { - utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); - // HACK: temporarily whitelist the new WETH token address `as if` they were - // already in the tokenRegistry. - // TODO: Remove this hack once we've updated the TokenRegistries - // Airtable task: https://airtable.com/tblFe0Q9JuKJPYbTn/viwsOG2Y97qdIeCIO/recv3VGmIorFzHBVz - if (configs.SHOULD_DEPRECATE_OLD_WETH_TOKEN && tokenAddress === configs.NEW_WRAPPED_ETHERS[this.networkId]) { - return true; - } - const tokenIfExists = await this._zeroEx.tokenRegistry.getTokenIfExistsAsync(tokenAddress); - return !_.isUndefined(tokenIfExists); - } - public getLedgerDerivationPathIfExists(): string { - if (_.isUndefined(this._ledgerSubprovider)) { - return undefined; - } - const path = this._ledgerSubprovider.getPath(); - return path; - } - public updateLedgerDerivationPathIfExists(path: string) { - if (_.isUndefined(this._ledgerSubprovider)) { - return; // noop - } - this._ledgerSubprovider.setPath(path); - } - public updateLedgerDerivationIndex(pathIndex: number) { - if (_.isUndefined(this._ledgerSubprovider)) { - return; // noop - } - this._ledgerSubprovider.setPathIndex(pathIndex); - } - public async providerTypeUpdatedFireAndForgetAsync(providerType: ProviderType) { - utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); - // Should actually be Web3.Provider|ProviderEngine union type but it causes issues - // later on in the logic. - let provider; - switch (providerType) { - case ProviderType.Ledger: { - const isU2FSupported = await utils.isU2FSupportedAsync(); - if (!isU2FSupported) { - throw new Error('Cannot update providerType to LEDGER without U2F support'); - } - - // Cache injected provider so that we can switch the user back to it easily - this._cachedProvider = this._web3Wrapper.getProviderObj(); - - this._dispatcher.updateUserAddress(''); // Clear old userAddress - - provider = new ProviderEngine(); - const ledgerWalletConfigs = { - networkId: this.networkId, - ledgerEthereumClientFactoryAsync: ledgerEthereumBrowserClientFactoryAsync, - }; - this._ledgerSubprovider = new LedgerSubprovider(ledgerWalletConfigs); - provider.addProvider(this._ledgerSubprovider); - provider.addProvider(new FilterSubprovider()); - const networkId = configs.IS_MAINNET_ENABLED - ? constants.NETWORK_ID_MAINNET - : constants.NETWORK_ID_TESTNET; - provider.addProvider(new RedundantRPCSubprovider(configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkId])); - provider.start(); - this._web3Wrapper.destroy(); - const shouldPollUserAddress = false; - this._web3Wrapper = new Web3Wrapper(this._dispatcher, provider, this.networkId, shouldPollUserAddress); - this._zeroEx.setProvider(provider, networkId); - await this._postInstantiationOrUpdatingProviderZeroExAsync(); - break; - } - - case ProviderType.Injected: { - if (_.isUndefined(this._cachedProvider)) { - return; // Going from injected to injected, so we noop - } - provider = this._cachedProvider; - const shouldPollUserAddress = true; - this._web3Wrapper = new Web3Wrapper(this._dispatcher, provider, this.networkId, shouldPollUserAddress); - this._zeroEx.setProvider(provider, this.networkId); - await this._postInstantiationOrUpdatingProviderZeroExAsync(); - delete this._ledgerSubprovider; - delete this._cachedProvider; - break; - } - - default: - throw utils.spawnSwitchErr('providerType', providerType); - } - - await this._fetchTokenInformationAsync(); - } - public async setProxyAllowanceAsync(token: Token, amountInBaseUnits: BigNumber): Promise { - utils.assert(this.isValidAddress(token.address), BlockchainCallErrs.TokenAddressIsInvalid); - utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); - utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); - - const txHash = await this._zeroEx.token.setProxyAllowanceAsync( - token.address, - this._userAddress, - amountInBaseUnits, - ); - await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); - const allowance = amountInBaseUnits; - this._dispatcher.replaceTokenAllowanceByAddress(token.address, allowance); - } - public async transferAsync(token: Token, toAddress: string, amountInBaseUnits: BigNumber): Promise { - const txHash = await this._zeroEx.token.transferAsync( - token.address, - this._userAddress, - toAddress, - amountInBaseUnits, - ); - await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); - const etherScanLinkIfExists = utils.getEtherScanLinkIfExists(txHash, this.networkId, EtherscanLinkSuffixes.Tx); - this._dispatcher.showFlashMessage( - React.createElement(TokenSendCompleted, { - etherScanLinkIfExists, - token, - toAddress, - amountInBaseUnits, - }), - ); - } - public portalOrderToSignedOrder( - maker: string, - taker: string, - makerTokenAddress: string, - takerTokenAddress: string, - makerTokenAmount: BigNumber, - takerTokenAmount: BigNumber, - makerFee: BigNumber, - takerFee: BigNumber, - expirationUnixTimestampSec: BigNumber, - feeRecipient: string, - signatureData: SignatureData, - salt: BigNumber, - ): SignedOrder { - const ecSignature = signatureData; - const exchangeContractAddress = this.getExchangeContractAddressIfExists(); - const takerOrNullAddress = _.isEmpty(taker) ? constants.NULL_ADDRESS : taker; - const signedOrder = { - ecSignature, - exchangeContractAddress, - expirationUnixTimestampSec, - feeRecipient, - maker, - makerFee, - makerTokenAddress, - makerTokenAmount, - salt, - taker: takerOrNullAddress, - takerFee, - takerTokenAddress, - takerTokenAmount, - }; - return signedOrder; - } - public async fillOrderAsync(signedOrder: SignedOrder, fillTakerTokenAmount: BigNumber): Promise { - utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); - - const shouldThrowOnInsufficientBalanceOrAllowance = true; - - const txHash = await this._zeroEx.exchange.fillOrderAsync( - signedOrder, - fillTakerTokenAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - this._userAddress, - ); - const receipt = await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); - const logs: Array> = receipt.logs as any; - this._zeroEx.exchange.throwLogErrorsAsErrors(logs); - const logFill = _.find(logs, { event: 'LogFill' }); - const args = (logFill.args as any) as LogFillContractEventArgs; - const filledTakerTokenAmount = args.filledTakerTokenAmount; - return filledTakerTokenAmount; - } - public async cancelOrderAsync(signedOrder: SignedOrder, cancelTakerTokenAmount: BigNumber): Promise { - const txHash = await this._zeroEx.exchange.cancelOrderAsync(signedOrder, cancelTakerTokenAmount); - const receipt = await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); - const logs: Array> = receipt.logs as any; - this._zeroEx.exchange.throwLogErrorsAsErrors(logs); - const logCancel = _.find(logs, { event: ExchangeEvents.LogCancel }); - const args = (logCancel.args as any) as LogCancelContractEventArgs; - const cancelledTakerTokenAmount = args.cancelledTakerTokenAmount; - return cancelledTakerTokenAmount; - } - public async getUnavailableTakerAmountAsync(orderHash: string): Promise { - utils.assert(ZeroEx.isValidOrderHash(orderHash), 'Must be valid orderHash'); - utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); - const unavailableTakerAmount = await this._zeroEx.exchange.getUnavailableTakerAmountAsync(orderHash); - return unavailableTakerAmount; - } - public getExchangeContractAddressIfExists() { - return this._exchangeAddress; - } - public async validateFillOrderThrowIfInvalidAsync( - signedOrder: SignedOrder, - fillTakerTokenAmount: BigNumber, - takerAddress: string, - ): Promise { - await this._zeroEx.exchange.validateFillOrderThrowIfInvalidAsync( - signedOrder, - fillTakerTokenAmount, - takerAddress, - ); - } - public async validateCancelOrderThrowIfInvalidAsync( - order: Order, - cancelTakerTokenAmount: BigNumber, - ): Promise { - await this._zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(order, cancelTakerTokenAmount); - } - public isValidAddress(address: string): boolean { - const lowercaseAddress = address.toLowerCase(); - return this._web3Wrapper.isAddress(lowercaseAddress); - } - public async pollTokenBalanceAsync(token: Token) { - utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); - - const [currBalance] = await this.getTokenBalanceAndAllowanceAsync(this._userAddress, token.address); - - this._zrxPollIntervalId = intervalUtils.setAsyncExcludingInterval( - async () => { - const [balance] = await this.getTokenBalanceAndAllowanceAsync(this._userAddress, token.address); - if (!balance.eq(currBalance)) { - this._dispatcher.replaceTokenBalanceByAddress(token.address, balance); - intervalUtils.clearAsyncExcludingInterval(this._zrxPollIntervalId); - delete this._zrxPollIntervalId; - } - }, - 5000, - (err: Error) => { - utils.consoleLog(`Polling tokenBalance failed: ${err}`); - intervalUtils.clearAsyncExcludingInterval(this._zrxPollIntervalId); - delete this._zrxPollIntervalId; - }, - ); - } - public async signOrderHashAsync(orderHash: string): Promise { - utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); - const makerAddress = this._userAddress; - // If makerAddress is undefined, this means they have a web3 instance injected into their browser - // but no account addresses associated with it. - if (_.isUndefined(makerAddress)) { - throw new Error('Tried to send a sign request but user has no associated addresses'); - } - const ecSignature = await this._zeroEx.signOrderHashAsync(orderHash, makerAddress); - const signatureData = _.extend({}, ecSignature, { - hash: orderHash, - }); - this._dispatcher.updateSignatureData(signatureData); - return signatureData; - } - public async mintTestTokensAsync(token: Token) { - utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); - - const mintableContract = await this._instantiateContractIfExistsAsync(MintableArtifacts, token.address); - await mintableContract.mint(constants.MINT_AMOUNT, { - from: this._userAddress, - }); - const balanceDelta = constants.MINT_AMOUNT; - this._dispatcher.updateTokenBalanceByAddress(token.address, balanceDelta); - } - public async getBalanceInEthAsync(owner: string): Promise { - const balance = await this._web3Wrapper.getBalanceInEthAsync(owner); - return balance; - } - public async convertEthToWrappedEthTokensAsync(etherTokenAddress: string, amount: BigNumber): Promise { - utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); - utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); - - const txHash = await this._zeroEx.etherToken.depositAsync(etherTokenAddress, amount, this._userAddress); - await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); - } - public async convertWrappedEthTokensToEthAsync(etherTokenAddress: string, amount: BigNumber): Promise { - utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); - utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); - - const txHash = await this._zeroEx.etherToken.withdrawAsync(etherTokenAddress, amount, this._userAddress); - await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); - } - public async doesContractExistAtAddressAsync(address: string) { - const doesContractExist = await this._web3Wrapper.doesContractExistAtAddressAsync(address); - return doesContractExist; - } - public async getCurrentUserTokenBalanceAndAllowanceAsync(tokenAddress: string): Promise { - const tokenBalanceAndAllowance = await this.getTokenBalanceAndAllowanceAsync(this._userAddress, tokenAddress); - return tokenBalanceAndAllowance; - } - public async getTokenBalanceAndAllowanceAsync(ownerAddress: string, tokenAddress: string): Promise { - utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); - - if (_.isEmpty(ownerAddress)) { - const zero = new BigNumber(0); - return [zero, zero]; - } - let balance = new BigNumber(0); - let allowance = new BigNumber(0); - if (this._doesUserAddressExist()) { - balance = await this._zeroEx.token.getBalanceAsync(tokenAddress, ownerAddress); - allowance = await this._zeroEx.token.getProxyAllowanceAsync(tokenAddress, ownerAddress); - } - return [balance, allowance]; - } - public async updateTokenBalancesAndAllowancesAsync(tokens: Token[]) { - const tokenStateByAddress: TokenStateByAddress = {}; - for (const token of tokens) { - let balance = new BigNumber(0); - let allowance = new BigNumber(0); - if (this._doesUserAddressExist()) { - [balance, allowance] = await this.getTokenBalanceAndAllowanceAsync(this._userAddress, token.address); - } - const tokenState = { - balance, - allowance, - }; - tokenStateByAddress[token.address] = tokenState; - } - this._dispatcher.updateTokenStateByAddress(tokenStateByAddress); - } - public async getUserAccountsAsync() { - utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); - const userAccountsIfExists = await this._zeroEx.getAvailableAddressesAsync(); - return userAccountsIfExists; - } - // HACK: When a user is using a Ledger, we simply dispatch the selected userAddress, which - // by-passes the web3Wrapper logic for updating the prevUserAddress. We therefore need to - // manually update it. This should only be called by the LedgerConfigDialog. - public updateWeb3WrapperPrevUserAddress(newUserAddress: string) { - this._web3Wrapper.updatePrevUserAddress(newUserAddress); - } - public destroy() { - intervalUtils.clearAsyncExcludingInterval(this._zrxPollIntervalId); - this._web3Wrapper.destroy(); - this._stopWatchingExchangeLogFillEvents(); - } - private async _showEtherScanLinkAndAwaitTransactionMinedAsync( - txHash: string, - ): Promise { - const etherScanLinkIfExists = utils.getEtherScanLinkIfExists(txHash, this.networkId, EtherscanLinkSuffixes.Tx); - this._dispatcher.showFlashMessage( - React.createElement(TransactionSubmitted, { - etherScanLinkIfExists, - }), - ); - const receipt = await this._zeroEx.awaitTransactionMinedAsync(txHash); - return receipt; - } - private _doesUserAddressExist(): boolean { - return this._userAddress !== ''; - } - private async _rehydrateStoreWithContractEvents() { - // Ensure we are only ever listening to one set of events - this._stopWatchingExchangeLogFillEvents(); - - if (!this._doesUserAddressExist()) { - return; // short-circuit - } - - if (!_.isUndefined(this._zeroEx)) { - // Since we do not have an index on the `taker` address and want to show - // transactions where an account is either the `maker` or `taker`, we loop - // through all fill events, and filter/cache them client-side. - const filterIndexObj = {}; - await this._startListeningForExchangeLogFillEventsAsync(filterIndexObj); - } - } - private async _startListeningForExchangeLogFillEventsAsync(indexFilterValues: IndexedFilterValues): Promise { - utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); - utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); - - // Fetch historical logs - await this._fetchHistoricalExchangeLogFillEventsAsync(indexFilterValues); - - // Start a subscription for new logs - this._zeroEx.exchange.subscribe( - ExchangeEvents.LogFill, - indexFilterValues, - async (err: Error, decodedLogEvent: DecodedLogEvent) => { - if (err) { - // Note: it's not entirely clear from the documentation which - // errors will be thrown by `watch`. For now, let's log the error - // to rollbar and stop watching when one occurs - // tslint:disable-next-line:no-floating-promises - errorReporter.reportAsync(err); // fire and forget - return; - } else { - const decodedLog = decodedLogEvent.log; - if (!this._doesLogEventInvolveUser(decodedLog)) { - return; // We aren't interested in the fill event - } - this._updateLatestFillsBlockIfNeeded(decodedLog.blockNumber); - const fill = await this._convertDecodedLogToFillAsync(decodedLog); - if (decodedLogEvent.isRemoved) { - tradeHistoryStorage.removeFillFromUser(this._userAddress, this.networkId, fill); - } else { - tradeHistoryStorage.addFillToUser(this._userAddress, this.networkId, fill); - } - } - }, - ); - } - private async _fetchHistoricalExchangeLogFillEventsAsync(indexFilterValues: IndexedFilterValues) { - const fromBlock = tradeHistoryStorage.getFillsLatestBlock(this._userAddress, this.networkId); - const blockRange: BlockRange = { - fromBlock, - toBlock: 'latest' as BlockParam, - }; - const decodedLogs = await this._zeroEx.exchange.getLogsAsync( - ExchangeEvents.LogFill, - blockRange, - indexFilterValues, - ); - for (const decodedLog of decodedLogs) { - if (!this._doesLogEventInvolveUser(decodedLog)) { - continue; // We aren't interested in the fill event - } - this._updateLatestFillsBlockIfNeeded(decodedLog.blockNumber); - const fill = await this._convertDecodedLogToFillAsync(decodedLog); - tradeHistoryStorage.addFillToUser(this._userAddress, this.networkId, fill); - } - } - private async _convertDecodedLogToFillAsync(decodedLog: LogWithDecodedArgs) { - const args = decodedLog.args; - const blockTimestamp = await this._web3Wrapper.getBlockTimestampAsync(decodedLog.blockHash); - const fill = { - filledTakerTokenAmount: args.filledTakerTokenAmount, - filledMakerTokenAmount: args.filledMakerTokenAmount, - logIndex: decodedLog.logIndex, - maker: args.maker, - orderHash: args.orderHash, - taker: args.taker, - makerToken: args.makerToken, - takerToken: args.takerToken, - paidMakerFee: args.paidMakerFee, - paidTakerFee: args.paidTakerFee, - transactionHash: decodedLog.transactionHash, - blockTimestamp, - }; - return fill; - } - private _doesLogEventInvolveUser(decodedLog: LogWithDecodedArgs) { - const args = decodedLog.args; - const isUserMakerOrTaker = args.maker === this._userAddress || args.taker === this._userAddress; - return isUserMakerOrTaker; - } - private _updateLatestFillsBlockIfNeeded(blockNumber: number) { - const isBlockPending = _.isNull(blockNumber); - if (!isBlockPending) { - // Hack: I've observed the behavior where a client won't register certain fill events - // and lowering the cache blockNumber fixes the issue. As a quick fix for now, simply - // set the cached blockNumber 50 below the one returned. This way, upon refreshing, a user - // would still attempt to re-fetch events from the previous 50 blocks, but won't need to - // re-fetch all events in all blocks. - // TODO: Debug if this is a race condition, and apply a more precise fix - const blockNumberToSet = - blockNumber - BLOCK_NUMBER_BACK_TRACK < 0 ? 0 : blockNumber - BLOCK_NUMBER_BACK_TRACK; - tradeHistoryStorage.setFillsLatestBlock(this._userAddress, this.networkId, blockNumberToSet); - } - } - private _stopWatchingExchangeLogFillEvents(): void { - this._zeroEx.exchange.unsubscribeAll(); - } - private async _getTokenRegistryTokensByAddressAsync(): Promise { - utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); - const tokenRegistryTokens = await this._zeroEx.tokenRegistry.getTokensAsync(); - - const tokenByAddress: TokenByAddress = {}; - _.each(tokenRegistryTokens, (t: ZeroExToken, i: number) => { - // HACK: For now we have a hard-coded list of iconUrls for the dummyTokens - // TODO: Refactor this out and pull the iconUrl directly from the TokenRegistry - const iconUrl = configs.ICON_URL_BY_SYMBOL[t.symbol]; - // HACK: Temporarily we hijack the WETH addresses fetched from the tokenRegistry - // so that we can take our time with actually updating it. This ensures that when - // we deploy the new WETH page, everyone will re-fill their trackedTokens with the - // new canonical WETH. - // TODO: Remove this hack once we've updated the TokenRegistries - // Airtable task: https://airtable.com/tblFe0Q9JuKJPYbTn/viwsOG2Y97qdIeCIO/recv3VGmIorFzHBVz - let address = t.address; - if (configs.SHOULD_DEPRECATE_OLD_WETH_TOKEN && t.symbol === 'WETH') { - const newEtherTokenAddressIfExists = configs.NEW_WRAPPED_ETHERS[this.networkId]; - if (!_.isUndefined(newEtherTokenAddressIfExists)) { - address = newEtherTokenAddressIfExists; - } - } - const token: Token = { - iconUrl, - address, - name: t.name, - symbol: t.symbol, - decimals: t.decimals, - isTracked: false, - isRegistered: true, - }; - tokenByAddress[token.address] = token; - }); - return tokenByAddress; - } - private async _onPageLoadInitFireAndForgetAsync() { - await Blockchain._onPageLoadAsync(); // wait for page to load - - // Hack: We need to know the networkId the injectedWeb3 is connected to (if it is defined) in - // order to properly instantiate the web3Wrapper. Since we must use the async call, we cannot - // retrieve it from within the web3Wrapper constructor. This is and should remain the only - // call to a web3 instance outside of web3Wrapper in the entire dapp. - // In addition, if the user has an injectedWeb3 instance that is disconnected from a backing - // Ethereum node, this call will throw. We need to handle this case gracefully - const injectedWeb3 = (window as any).web3; - let networkIdIfExists: number; - if (!_.isUndefined(injectedWeb3)) { - try { - networkIdIfExists = _.parseInt(await promisify(injectedWeb3.version.getNetwork)()); - } catch (err) { - // Ignore error and proceed with networkId undefined - } - } - - const provider = await Blockchain._getProviderAsync(injectedWeb3, networkIdIfExists); - const networkId = !_.isUndefined(networkIdIfExists) - ? networkIdIfExists - : configs.IS_MAINNET_ENABLED ? constants.NETWORK_ID_MAINNET : constants.NETWORK_ID_TESTNET; - const zeroExConfigs = { - networkId, - }; - this._zeroEx = new ZeroEx(provider, zeroExConfigs); - this._updateProviderName(injectedWeb3); - const shouldPollUserAddress = true; - this._web3Wrapper = new Web3Wrapper(this._dispatcher, provider, networkId, shouldPollUserAddress); - await this._postInstantiationOrUpdatingProviderZeroExAsync(); - } - // This method should always be run after instantiating or updating the provider - // of the ZeroEx instance. - private async _postInstantiationOrUpdatingProviderZeroExAsync() { - utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); - this._exchangeAddress = this._zeroEx.exchange.getContractAddress(); - } - private _updateProviderName(injectedWeb3: Web3) { - const doesInjectedWeb3Exist = !_.isUndefined(injectedWeb3); - const providerName = doesInjectedWeb3Exist - ? Blockchain._getNameGivenProvider(injectedWeb3.currentProvider) - : constants.PROVIDER_NAME_PUBLIC; - this._dispatcher.updateInjectedProviderName(providerName); - } - private async _fetchTokenInformationAsync() { - utils.assert( - !_.isUndefined(this.networkId), - 'Cannot call fetchTokenInformationAsync if disconnected from Ethereum node', - ); - - this._dispatcher.updateBlockchainIsLoaded(false); - this._dispatcher.clearTokenByAddress(); - - const tokenRegistryTokensByAddress = await this._getTokenRegistryTokensByAddressAsync(); - - // HACK: We need to fetch the userAddress here because otherwise we cannot save the - // tracked tokens in localStorage under the users address nor fetch the token - // balances and allowances and we need to do this in order not to trigger the blockchain - // loading dialog to show up twice. First to load the contracts, and second to load the - // balances and allowances. - this._userAddress = await this._web3Wrapper.getFirstAccountIfExistsAsync(); - if (!_.isEmpty(this._userAddress)) { - this._dispatcher.updateUserAddress(this._userAddress); - } - - let trackedTokensIfExists = trackedTokenStorage.getTrackedTokensIfExists(this._userAddress, this.networkId); - const tokenRegistryTokens = _.values(tokenRegistryTokensByAddress); - if (_.isUndefined(trackedTokensIfExists)) { - trackedTokensIfExists = _.map(configs.DEFAULT_TRACKED_TOKEN_SYMBOLS, symbol => { - const token = _.find(tokenRegistryTokens, t => t.symbol === symbol); - token.isTracked = true; - return token; - }); - _.each(trackedTokensIfExists, token => { - trackedTokenStorage.addTrackedTokenToUser(this._userAddress, this.networkId, token); - }); - } else { - // Properly set all tokenRegistry tokens `isTracked` to true if they are in the existing trackedTokens array - _.each(trackedTokensIfExists, trackedToken => { - if (!_.isUndefined(tokenRegistryTokensByAddress[trackedToken.address])) { - tokenRegistryTokensByAddress[trackedToken.address].isTracked = true; - } - }); - } - const allTokens = _.uniq([...tokenRegistryTokens, ...trackedTokensIfExists]); - this._dispatcher.updateTokenByAddress(allTokens); - - // Get balance/allowance for tracked tokens - await this.updateTokenBalancesAndAllowancesAsync(trackedTokensIfExists); - - const mostPopularTradingPairTokens: Token[] = [ - _.find(allTokens, { symbol: configs.DEFAULT_TRACKED_TOKEN_SYMBOLS[0] }), - _.find(allTokens, { symbol: configs.DEFAULT_TRACKED_TOKEN_SYMBOLS[1] }), - ]; - this._dispatcher.updateChosenAssetTokenAddress(Side.Deposit, mostPopularTradingPairTokens[0].address); - this._dispatcher.updateChosenAssetTokenAddress(Side.Receive, mostPopularTradingPairTokens[1].address); - this._dispatcher.updateBlockchainIsLoaded(true); - } - private async _instantiateContractIfExistsAsync(artifact: any, address?: string): Promise { - const c = await contract(artifact); - const providerObj = this._web3Wrapper.getProviderObj(); - c.setProvider(providerObj); - - const artifactNetworkConfigs = artifact.networks[this.networkId]; - let contractAddress; - if (!_.isUndefined(address)) { - contractAddress = address; - } else if (!_.isUndefined(artifactNetworkConfigs)) { - contractAddress = artifactNetworkConfigs.address; - } - - if (!_.isUndefined(contractAddress)) { - const doesContractExist = await this.doesContractExistAtAddressAsync(contractAddress); - if (!doesContractExist) { - utils.consoleLog(`Contract does not exist: ${artifact.contract_name} at ${contractAddress}`); - throw new Error(BlockchainCallErrs.ContractDoesNotExist); - } - } - - try { - const contractInstance = _.isUndefined(address) ? await c.deployed() : await c.at(address); - return contractInstance; - } catch (err) { - const errMsg = `${err}`; - utils.consoleLog(`Notice: Error encountered: ${err} ${err.stack}`); - if (_.includes(errMsg, 'not been deployed to detected network')) { - throw new Error(BlockchainCallErrs.ContractDoesNotExist); - } else { - await errorReporter.reportAsync(err); - throw new Error(BlockchainCallErrs.UnhandledError); - } - } - } + public networkId: number; + public nodeVersion: string; + private _zeroEx: ZeroEx; + private _dispatcher: Dispatcher; + private _web3Wrapper?: Web3Wrapper; + private _exchangeAddress: string; + private _userAddress: string; + private _cachedProvider: Web3.Provider; + private _ledgerSubprovider: LedgerWalletSubprovider; + private _zrxPollIntervalId: NodeJS.Timer; + private static async _onPageLoadAsync(): Promise { + if (document.readyState === 'complete') { + return; // Already loaded + } + return new Promise((resolve, reject) => { + window.onload = () => resolve(); + }); + } + private static _getNameGivenProvider(provider: Web3.Provider): string { + if (!_.isUndefined((provider as any).isMetaMask)) { + return constants.PROVIDER_NAME_METAMASK; + } + + // HACK: We use the fact that Parity Signer's provider is an instance of their + // internal `Web3FrameProvider` class. + const isParitySigner = _.startsWith(provider.constructor.toString(), 'function Web3FrameProvider'); + if (isParitySigner) { + return constants.PROVIDER_NAME_PARITY_SIGNER; + } + + return constants.PROVIDER_NAME_GENERIC; + } + private static async _getProviderAsync(injectedWeb3: Web3, networkIdIfExists: number) { + const doesInjectedWeb3Exist = !_.isUndefined(injectedWeb3); + const publicNodeUrlsIfExistsForNetworkId = configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkIdIfExists]; + const isPublicNodeAvailableForNetworkId = !_.isUndefined(publicNodeUrlsIfExistsForNetworkId); + + let provider; + if (doesInjectedWeb3Exist && isPublicNodeAvailableForNetworkId) { + // We catch all requests involving a users account and send it to the injectedWeb3 + // instance. All other requests go to the public hosted node. + provider = new ProviderEngine(); + provider.addProvider(new InjectedWeb3Subprovider(injectedWeb3)); + provider.addProvider(new FilterSubprovider()); + provider.addProvider(new RedundantRPCSubprovider(publicNodeUrlsIfExistsForNetworkId)); + provider.start(); + } else if (doesInjectedWeb3Exist) { + // Since no public node for this network, all requests go to injectedWeb3 instance + provider = injectedWeb3.currentProvider; + } else { + // If no injectedWeb3 instance, all requests fallback to our public hosted mainnet/testnet node + // We do this so that users can still browse the 0x Portal DApp even if they do not have web3 + // injected into their browser. + provider = new ProviderEngine(); + provider.addProvider(new FilterSubprovider()); + const networkId = configs.IS_MAINNET_ENABLED ? constants.NETWORK_ID_MAINNET : constants.NETWORK_ID_TESTNET; + provider.addProvider(new RedundantRPCSubprovider(configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkId])); + provider.start(); + } + + return provider; + } + constructor(dispatcher: Dispatcher, isSalePage: boolean = false) { + this._dispatcher = dispatcher; + this._userAddress = ''; + // tslint:disable-next-line:no-floating-promises + this._onPageLoadInitFireAndForgetAsync(); + } + public async networkIdUpdatedFireAndForgetAsync(newNetworkId: number) { + const isConnected = !_.isUndefined(newNetworkId); + if (!isConnected) { + this.networkId = newNetworkId; + this._dispatcher.encounteredBlockchainError(BlockchainErrs.DisconnectedFromEthereumNode); + this._dispatcher.updateShouldBlockchainErrDialogBeOpen(true); + } else if (this.networkId !== newNetworkId) { + this.networkId = newNetworkId; + this._dispatcher.encounteredBlockchainError(BlockchainErrs.NoError); + await this._fetchTokenInformationAsync(); + await this._rehydrateStoreWithContractEvents(); + } + } + public async userAddressUpdatedFireAndForgetAsync(newUserAddress: string) { + if (this._userAddress !== newUserAddress) { + this._userAddress = newUserAddress; + await this._fetchTokenInformationAsync(); + await this._rehydrateStoreWithContractEvents(); + } + } + public async nodeVersionUpdatedFireAndForgetAsync(nodeVersion: string) { + if (this.nodeVersion !== nodeVersion) { + this.nodeVersion = nodeVersion; + } + } + public async isAddressInTokenRegistryAsync(tokenAddress: string): Promise { + utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); + // HACK: temporarily whitelist the new WETH token address `as if` they were + // already in the tokenRegistry. + // TODO: Remove this hack once we've updated the TokenRegistries + // Airtable task: https://airtable.com/tblFe0Q9JuKJPYbTn/viwsOG2Y97qdIeCIO/recv3VGmIorFzHBVz + if (configs.SHOULD_DEPRECATE_OLD_WETH_TOKEN && tokenAddress === configs.NEW_WRAPPED_ETHERS[this.networkId]) { + return true; + } + const tokenIfExists = await this._zeroEx.tokenRegistry.getTokenIfExistsAsync(tokenAddress); + return !_.isUndefined(tokenIfExists); + } + public getLedgerDerivationPathIfExists(): string { + if (_.isUndefined(this._ledgerSubprovider)) { + return undefined; + } + const path = this._ledgerSubprovider.getPath(); + return path; + } + public updateLedgerDerivationPathIfExists(path: string) { + if (_.isUndefined(this._ledgerSubprovider)) { + return; // noop + } + this._ledgerSubprovider.setPath(path); + } + public updateLedgerDerivationIndex(pathIndex: number) { + if (_.isUndefined(this._ledgerSubprovider)) { + return; // noop + } + this._ledgerSubprovider.setPathIndex(pathIndex); + } + public async providerTypeUpdatedFireAndForgetAsync(providerType: ProviderType) { + utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); + // Should actually be Web3.Provider|ProviderEngine union type but it causes issues + // later on in the logic. + let provider; + switch (providerType) { + case ProviderType.Ledger: { + const isU2FSupported = await utils.isU2FSupportedAsync(); + if (!isU2FSupported) { + throw new Error('Cannot update providerType to LEDGER without U2F support'); + } + + // Cache injected provider so that we can switch the user back to it easily + this._cachedProvider = this._web3Wrapper.getProviderObj(); + + this._dispatcher.updateUserAddress(''); // Clear old userAddress + + provider = new ProviderEngine(); + const ledgerWalletConfigs = { + networkId: this.networkId, + ledgerEthereumClientFactoryAsync: ledgerEthereumBrowserClientFactoryAsync, + }; + this._ledgerSubprovider = new LedgerSubprovider(ledgerWalletConfigs); + provider.addProvider(this._ledgerSubprovider); + provider.addProvider(new FilterSubprovider()); + const networkId = configs.IS_MAINNET_ENABLED + ? constants.NETWORK_ID_MAINNET + : constants.NETWORK_ID_TESTNET; + provider.addProvider(new RedundantRPCSubprovider(configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkId])); + provider.start(); + this._web3Wrapper.destroy(); + const shouldPollUserAddress = false; + this._web3Wrapper = new Web3Wrapper(this._dispatcher, provider, this.networkId, shouldPollUserAddress); + this._zeroEx.setProvider(provider, networkId); + await this._postInstantiationOrUpdatingProviderZeroExAsync(); + break; + } + + case ProviderType.Injected: { + if (_.isUndefined(this._cachedProvider)) { + return; // Going from injected to injected, so we noop + } + provider = this._cachedProvider; + const shouldPollUserAddress = true; + this._web3Wrapper = new Web3Wrapper(this._dispatcher, provider, this.networkId, shouldPollUserAddress); + this._zeroEx.setProvider(provider, this.networkId); + await this._postInstantiationOrUpdatingProviderZeroExAsync(); + delete this._ledgerSubprovider; + delete this._cachedProvider; + break; + } + + default: + throw utils.spawnSwitchErr('providerType', providerType); + } + + await this._fetchTokenInformationAsync(); + } + public async setProxyAllowanceAsync(token: Token, amountInBaseUnits: BigNumber): Promise { + utils.assert(this.isValidAddress(token.address), BlockchainCallErrs.TokenAddressIsInvalid); + utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); + utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); + + const txHash = await this._zeroEx.token.setProxyAllowanceAsync( + token.address, + this._userAddress, + amountInBaseUnits, + ); + await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); + const allowance = amountInBaseUnits; + this._dispatcher.replaceTokenAllowanceByAddress(token.address, allowance); + } + public async transferAsync(token: Token, toAddress: string, amountInBaseUnits: BigNumber): Promise { + const txHash = await this._zeroEx.token.transferAsync( + token.address, + this._userAddress, + toAddress, + amountInBaseUnits, + ); + await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); + const etherScanLinkIfExists = utils.getEtherScanLinkIfExists(txHash, this.networkId, EtherscanLinkSuffixes.Tx); + this._dispatcher.showFlashMessage( + React.createElement(TokenSendCompleted, { + etherScanLinkIfExists, + token, + toAddress, + amountInBaseUnits, + }), + ); + } + public portalOrderToSignedOrder( + maker: string, + taker: string, + makerTokenAddress: string, + takerTokenAddress: string, + makerTokenAmount: BigNumber, + takerTokenAmount: BigNumber, + makerFee: BigNumber, + takerFee: BigNumber, + expirationUnixTimestampSec: BigNumber, + feeRecipient: string, + signatureData: SignatureData, + salt: BigNumber, + ): SignedOrder { + const ecSignature = signatureData; + const exchangeContractAddress = this.getExchangeContractAddressIfExists(); + const takerOrNullAddress = _.isEmpty(taker) ? constants.NULL_ADDRESS : taker; + const signedOrder = { + ecSignature, + exchangeContractAddress, + expirationUnixTimestampSec, + feeRecipient, + maker, + makerFee, + makerTokenAddress, + makerTokenAmount, + salt, + taker: takerOrNullAddress, + takerFee, + takerTokenAddress, + takerTokenAmount, + }; + return signedOrder; + } + public async fillOrderAsync(signedOrder: SignedOrder, fillTakerTokenAmount: BigNumber): Promise { + utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); + + const shouldThrowOnInsufficientBalanceOrAllowance = true; + + const txHash = await this._zeroEx.exchange.fillOrderAsync( + signedOrder, + fillTakerTokenAmount, + shouldThrowOnInsufficientBalanceOrAllowance, + this._userAddress, + ); + const receipt = await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); + const logs: Array> = receipt.logs as any; + this._zeroEx.exchange.throwLogErrorsAsErrors(logs); + const logFill = _.find(logs, { event: 'LogFill' }); + const args = (logFill.args as any) as LogFillContractEventArgs; + const filledTakerTokenAmount = args.filledTakerTokenAmount; + return filledTakerTokenAmount; + } + public async cancelOrderAsync(signedOrder: SignedOrder, cancelTakerTokenAmount: BigNumber): Promise { + const txHash = await this._zeroEx.exchange.cancelOrderAsync(signedOrder, cancelTakerTokenAmount); + const receipt = await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); + const logs: Array> = receipt.logs as any; + this._zeroEx.exchange.throwLogErrorsAsErrors(logs); + const logCancel = _.find(logs, { event: ExchangeEvents.LogCancel }); + const args = (logCancel.args as any) as LogCancelContractEventArgs; + const cancelledTakerTokenAmount = args.cancelledTakerTokenAmount; + return cancelledTakerTokenAmount; + } + public async getUnavailableTakerAmountAsync(orderHash: string): Promise { + utils.assert(ZeroEx.isValidOrderHash(orderHash), 'Must be valid orderHash'); + utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); + const unavailableTakerAmount = await this._zeroEx.exchange.getUnavailableTakerAmountAsync(orderHash); + return unavailableTakerAmount; + } + public getExchangeContractAddressIfExists() { + return this._exchangeAddress; + } + public async validateFillOrderThrowIfInvalidAsync( + signedOrder: SignedOrder, + fillTakerTokenAmount: BigNumber, + takerAddress: string, + ): Promise { + await this._zeroEx.exchange.validateFillOrderThrowIfInvalidAsync( + signedOrder, + fillTakerTokenAmount, + takerAddress, + ); + } + public async validateCancelOrderThrowIfInvalidAsync( + order: Order, + cancelTakerTokenAmount: BigNumber, + ): Promise { + await this._zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(order, cancelTakerTokenAmount); + } + public isValidAddress(address: string): boolean { + const lowercaseAddress = address.toLowerCase(); + return this._web3Wrapper.isAddress(lowercaseAddress); + } + public async pollTokenBalanceAsync(token: Token) { + utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); + + const [currBalance] = await this.getTokenBalanceAndAllowanceAsync(this._userAddress, token.address); + + this._zrxPollIntervalId = intervalUtils.setAsyncExcludingInterval( + async () => { + const [balance] = await this.getTokenBalanceAndAllowanceAsync(this._userAddress, token.address); + if (!balance.eq(currBalance)) { + this._dispatcher.replaceTokenBalanceByAddress(token.address, balance); + intervalUtils.clearAsyncExcludingInterval(this._zrxPollIntervalId); + delete this._zrxPollIntervalId; + } + }, + 5000, + (err: Error) => { + utils.consoleLog(`Polling tokenBalance failed: ${err}`); + intervalUtils.clearAsyncExcludingInterval(this._zrxPollIntervalId); + delete this._zrxPollIntervalId; + }, + ); + } + public async signOrderHashAsync(orderHash: string): Promise { + utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); + const makerAddress = this._userAddress; + // If makerAddress is undefined, this means they have a web3 instance injected into their browser + // but no account addresses associated with it. + if (_.isUndefined(makerAddress)) { + throw new Error('Tried to send a sign request but user has no associated addresses'); + } + const ecSignature = await this._zeroEx.signOrderHashAsync(orderHash, makerAddress); + const signatureData = _.extend({}, ecSignature, { + hash: orderHash, + }); + this._dispatcher.updateSignatureData(signatureData); + return signatureData; + } + public async mintTestTokensAsync(token: Token) { + utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); + + const mintableContract = await this._instantiateContractIfExistsAsync(MintableArtifacts, token.address); + await mintableContract.mint(constants.MINT_AMOUNT, { + from: this._userAddress, + }); + const balanceDelta = constants.MINT_AMOUNT; + this._dispatcher.updateTokenBalanceByAddress(token.address, balanceDelta); + } + public async getBalanceInEthAsync(owner: string): Promise { + const balance = await this._web3Wrapper.getBalanceInEthAsync(owner); + return balance; + } + public async convertEthToWrappedEthTokensAsync(etherTokenAddress: string, amount: BigNumber): Promise { + utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); + utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); + + const txHash = await this._zeroEx.etherToken.depositAsync(etherTokenAddress, amount, this._userAddress); + await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); + } + public async convertWrappedEthTokensToEthAsync(etherTokenAddress: string, amount: BigNumber): Promise { + utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); + utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); + + const txHash = await this._zeroEx.etherToken.withdrawAsync(etherTokenAddress, amount, this._userAddress); + await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); + } + public async doesContractExistAtAddressAsync(address: string) { + const doesContractExist = await this._web3Wrapper.doesContractExistAtAddressAsync(address); + return doesContractExist; + } + public async getCurrentUserTokenBalanceAndAllowanceAsync(tokenAddress: string): Promise { + const tokenBalanceAndAllowance = await this.getTokenBalanceAndAllowanceAsync(this._userAddress, tokenAddress); + return tokenBalanceAndAllowance; + } + public async getTokenBalanceAndAllowanceAsync(ownerAddress: string, tokenAddress: string): Promise { + utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); + + if (_.isEmpty(ownerAddress)) { + const zero = new BigNumber(0); + return [zero, zero]; + } + let balance = new BigNumber(0); + let allowance = new BigNumber(0); + if (this._doesUserAddressExist()) { + balance = await this._zeroEx.token.getBalanceAsync(tokenAddress, ownerAddress); + allowance = await this._zeroEx.token.getProxyAllowanceAsync(tokenAddress, ownerAddress); + } + return [balance, allowance]; + } + public async updateTokenBalancesAndAllowancesAsync(tokens: Token[]) { + const tokenStateByAddress: TokenStateByAddress = {}; + for (const token of tokens) { + let balance = new BigNumber(0); + let allowance = new BigNumber(0); + if (this._doesUserAddressExist()) { + [balance, allowance] = await this.getTokenBalanceAndAllowanceAsync(this._userAddress, token.address); + } + const tokenState = { + balance, + allowance, + }; + tokenStateByAddress[token.address] = tokenState; + } + this._dispatcher.updateTokenStateByAddress(tokenStateByAddress); + } + public async getUserAccountsAsync() { + utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); + const userAccountsIfExists = await this._zeroEx.getAvailableAddressesAsync(); + return userAccountsIfExists; + } + // HACK: When a user is using a Ledger, we simply dispatch the selected userAddress, which + // by-passes the web3Wrapper logic for updating the prevUserAddress. We therefore need to + // manually update it. This should only be called by the LedgerConfigDialog. + public updateWeb3WrapperPrevUserAddress(newUserAddress: string) { + this._web3Wrapper.updatePrevUserAddress(newUserAddress); + } + public destroy() { + intervalUtils.clearAsyncExcludingInterval(this._zrxPollIntervalId); + this._web3Wrapper.destroy(); + this._stopWatchingExchangeLogFillEvents(); + } + private async _showEtherScanLinkAndAwaitTransactionMinedAsync( + txHash: string, + ): Promise { + const etherScanLinkIfExists = utils.getEtherScanLinkIfExists(txHash, this.networkId, EtherscanLinkSuffixes.Tx); + this._dispatcher.showFlashMessage( + React.createElement(TransactionSubmitted, { + etherScanLinkIfExists, + }), + ); + const receipt = await this._zeroEx.awaitTransactionMinedAsync(txHash); + return receipt; + } + private _doesUserAddressExist(): boolean { + return this._userAddress !== ''; + } + private async _rehydrateStoreWithContractEvents() { + // Ensure we are only ever listening to one set of events + this._stopWatchingExchangeLogFillEvents(); + + if (!this._doesUserAddressExist()) { + return; // short-circuit + } + + if (!_.isUndefined(this._zeroEx)) { + // Since we do not have an index on the `taker` address and want to show + // transactions where an account is either the `maker` or `taker`, we loop + // through all fill events, and filter/cache them client-side. + const filterIndexObj = {}; + await this._startListeningForExchangeLogFillEventsAsync(filterIndexObj); + } + } + private async _startListeningForExchangeLogFillEventsAsync(indexFilterValues: IndexedFilterValues): Promise { + utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); + utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); + + // Fetch historical logs + await this._fetchHistoricalExchangeLogFillEventsAsync(indexFilterValues); + + // Start a subscription for new logs + this._zeroEx.exchange.subscribe( + ExchangeEvents.LogFill, + indexFilterValues, + async (err: Error, decodedLogEvent: DecodedLogEvent) => { + if (err) { + // Note: it's not entirely clear from the documentation which + // errors will be thrown by `watch`. For now, let's log the error + // to rollbar and stop watching when one occurs + // tslint:disable-next-line:no-floating-promises + errorReporter.reportAsync(err); // fire and forget + return; + } else { + const decodedLog = decodedLogEvent.log; + if (!this._doesLogEventInvolveUser(decodedLog)) { + return; // We aren't interested in the fill event + } + this._updateLatestFillsBlockIfNeeded(decodedLog.blockNumber); + const fill = await this._convertDecodedLogToFillAsync(decodedLog); + if (decodedLogEvent.isRemoved) { + tradeHistoryStorage.removeFillFromUser(this._userAddress, this.networkId, fill); + } else { + tradeHistoryStorage.addFillToUser(this._userAddress, this.networkId, fill); + } + } + }, + ); + } + private async _fetchHistoricalExchangeLogFillEventsAsync(indexFilterValues: IndexedFilterValues) { + const fromBlock = tradeHistoryStorage.getFillsLatestBlock(this._userAddress, this.networkId); + const blockRange: BlockRange = { + fromBlock, + toBlock: 'latest' as BlockParam, + }; + const decodedLogs = await this._zeroEx.exchange.getLogsAsync( + ExchangeEvents.LogFill, + blockRange, + indexFilterValues, + ); + for (const decodedLog of decodedLogs) { + if (!this._doesLogEventInvolveUser(decodedLog)) { + continue; // We aren't interested in the fill event + } + this._updateLatestFillsBlockIfNeeded(decodedLog.blockNumber); + const fill = await this._convertDecodedLogToFillAsync(decodedLog); + tradeHistoryStorage.addFillToUser(this._userAddress, this.networkId, fill); + } + } + private async _convertDecodedLogToFillAsync(decodedLog: LogWithDecodedArgs) { + const args = decodedLog.args; + const blockTimestamp = await this._web3Wrapper.getBlockTimestampAsync(decodedLog.blockHash); + const fill = { + filledTakerTokenAmount: args.filledTakerTokenAmount, + filledMakerTokenAmount: args.filledMakerTokenAmount, + logIndex: decodedLog.logIndex, + maker: args.maker, + orderHash: args.orderHash, + taker: args.taker, + makerToken: args.makerToken, + takerToken: args.takerToken, + paidMakerFee: args.paidMakerFee, + paidTakerFee: args.paidTakerFee, + transactionHash: decodedLog.transactionHash, + blockTimestamp, + }; + return fill; + } + private _doesLogEventInvolveUser(decodedLog: LogWithDecodedArgs) { + const args = decodedLog.args; + const isUserMakerOrTaker = args.maker === this._userAddress || args.taker === this._userAddress; + return isUserMakerOrTaker; + } + private _updateLatestFillsBlockIfNeeded(blockNumber: number) { + const isBlockPending = _.isNull(blockNumber); + if (!isBlockPending) { + // Hack: I've observed the behavior where a client won't register certain fill events + // and lowering the cache blockNumber fixes the issue. As a quick fix for now, simply + // set the cached blockNumber 50 below the one returned. This way, upon refreshing, a user + // would still attempt to re-fetch events from the previous 50 blocks, but won't need to + // re-fetch all events in all blocks. + // TODO: Debug if this is a race condition, and apply a more precise fix + const blockNumberToSet = + blockNumber - BLOCK_NUMBER_BACK_TRACK < 0 ? 0 : blockNumber - BLOCK_NUMBER_BACK_TRACK; + tradeHistoryStorage.setFillsLatestBlock(this._userAddress, this.networkId, blockNumberToSet); + } + } + private _stopWatchingExchangeLogFillEvents(): void { + this._zeroEx.exchange.unsubscribeAll(); + } + private async _getTokenRegistryTokensByAddressAsync(): Promise { + utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); + const tokenRegistryTokens = await this._zeroEx.tokenRegistry.getTokensAsync(); + + const tokenByAddress: TokenByAddress = {}; + _.each(tokenRegistryTokens, (t: ZeroExToken, i: number) => { + // HACK: For now we have a hard-coded list of iconUrls for the dummyTokens + // TODO: Refactor this out and pull the iconUrl directly from the TokenRegistry + const iconUrl = configs.ICON_URL_BY_SYMBOL[t.symbol]; + // HACK: Temporarily we hijack the WETH addresses fetched from the tokenRegistry + // so that we can take our time with actually updating it. This ensures that when + // we deploy the new WETH page, everyone will re-fill their trackedTokens with the + // new canonical WETH. + // TODO: Remove this hack once we've updated the TokenRegistries + // Airtable task: https://airtable.com/tblFe0Q9JuKJPYbTn/viwsOG2Y97qdIeCIO/recv3VGmIorFzHBVz + let address = t.address; + if (configs.SHOULD_DEPRECATE_OLD_WETH_TOKEN && t.symbol === 'WETH') { + const newEtherTokenAddressIfExists = configs.NEW_WRAPPED_ETHERS[this.networkId]; + if (!_.isUndefined(newEtherTokenAddressIfExists)) { + address = newEtherTokenAddressIfExists; + } + } + const token: Token = { + iconUrl, + address, + name: t.name, + symbol: t.symbol, + decimals: t.decimals, + isTracked: false, + isRegistered: true, + }; + tokenByAddress[token.address] = token; + }); + return tokenByAddress; + } + private async _onPageLoadInitFireAndForgetAsync() { + await Blockchain._onPageLoadAsync(); // wait for page to load + + // Hack: We need to know the networkId the injectedWeb3 is connected to (if it is defined) in + // order to properly instantiate the web3Wrapper. Since we must use the async call, we cannot + // retrieve it from within the web3Wrapper constructor. This is and should remain the only + // call to a web3 instance outside of web3Wrapper in the entire dapp. + // In addition, if the user has an injectedWeb3 instance that is disconnected from a backing + // Ethereum node, this call will throw. We need to handle this case gracefully + const injectedWeb3 = (window as any).web3; + let networkIdIfExists: number; + if (!_.isUndefined(injectedWeb3)) { + try { + networkIdIfExists = _.parseInt(await promisify(injectedWeb3.version.getNetwork)()); + } catch (err) { + // Ignore error and proceed with networkId undefined + } + } + + const provider = await Blockchain._getProviderAsync(injectedWeb3, networkIdIfExists); + const networkId = !_.isUndefined(networkIdIfExists) + ? networkIdIfExists + : configs.IS_MAINNET_ENABLED ? constants.NETWORK_ID_MAINNET : constants.NETWORK_ID_TESTNET; + const zeroExConfigs = { + networkId, + }; + this._zeroEx = new ZeroEx(provider, zeroExConfigs); + this._updateProviderName(injectedWeb3); + const shouldPollUserAddress = true; + this._web3Wrapper = new Web3Wrapper(this._dispatcher, provider, networkId, shouldPollUserAddress); + await this._postInstantiationOrUpdatingProviderZeroExAsync(); + } + // This method should always be run after instantiating or updating the provider + // of the ZeroEx instance. + private async _postInstantiationOrUpdatingProviderZeroExAsync() { + utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); + this._exchangeAddress = this._zeroEx.exchange.getContractAddress(); + } + private _updateProviderName(injectedWeb3: Web3) { + const doesInjectedWeb3Exist = !_.isUndefined(injectedWeb3); + const providerName = doesInjectedWeb3Exist + ? Blockchain._getNameGivenProvider(injectedWeb3.currentProvider) + : constants.PROVIDER_NAME_PUBLIC; + this._dispatcher.updateInjectedProviderName(providerName); + } + private async _fetchTokenInformationAsync() { + utils.assert( + !_.isUndefined(this.networkId), + 'Cannot call fetchTokenInformationAsync if disconnected from Ethereum node', + ); + + this._dispatcher.updateBlockchainIsLoaded(false); + this._dispatcher.clearTokenByAddress(); + + const tokenRegistryTokensByAddress = await this._getTokenRegistryTokensByAddressAsync(); + + // HACK: We need to fetch the userAddress here because otherwise we cannot save the + // tracked tokens in localStorage under the users address nor fetch the token + // balances and allowances and we need to do this in order not to trigger the blockchain + // loading dialog to show up twice. First to load the contracts, and second to load the + // balances and allowances. + this._userAddress = await this._web3Wrapper.getFirstAccountIfExistsAsync(); + if (!_.isEmpty(this._userAddress)) { + this._dispatcher.updateUserAddress(this._userAddress); + } + + let trackedTokensIfExists = trackedTokenStorage.getTrackedTokensIfExists(this._userAddress, this.networkId); + const tokenRegistryTokens = _.values(tokenRegistryTokensByAddress); + if (_.isUndefined(trackedTokensIfExists)) { + trackedTokensIfExists = _.map(configs.DEFAULT_TRACKED_TOKEN_SYMBOLS, symbol => { + const token = _.find(tokenRegistryTokens, t => t.symbol === symbol); + token.isTracked = true; + return token; + }); + _.each(trackedTokensIfExists, token => { + trackedTokenStorage.addTrackedTokenToUser(this._userAddress, this.networkId, token); + }); + } else { + // Properly set all tokenRegistry tokens `isTracked` to true if they are in the existing trackedTokens array + _.each(trackedTokensIfExists, trackedToken => { + if (!_.isUndefined(tokenRegistryTokensByAddress[trackedToken.address])) { + tokenRegistryTokensByAddress[trackedToken.address].isTracked = true; + } + }); + } + const allTokens = _.uniq([...tokenRegistryTokens, ...trackedTokensIfExists]); + this._dispatcher.updateTokenByAddress(allTokens); + + // Get balance/allowance for tracked tokens + await this.updateTokenBalancesAndAllowancesAsync(trackedTokensIfExists); + + const mostPopularTradingPairTokens: Token[] = [ + _.find(allTokens, { symbol: configs.DEFAULT_TRACKED_TOKEN_SYMBOLS[0] }), + _.find(allTokens, { symbol: configs.DEFAULT_TRACKED_TOKEN_SYMBOLS[1] }), + ]; + this._dispatcher.updateChosenAssetTokenAddress(Side.Deposit, mostPopularTradingPairTokens[0].address); + this._dispatcher.updateChosenAssetTokenAddress(Side.Receive, mostPopularTradingPairTokens[1].address); + this._dispatcher.updateBlockchainIsLoaded(true); + } + private async _instantiateContractIfExistsAsync(artifact: any, address?: string): Promise { + const c = await contract(artifact); + const providerObj = this._web3Wrapper.getProviderObj(); + c.setProvider(providerObj); + + const artifactNetworkConfigs = artifact.networks[this.networkId]; + let contractAddress; + if (!_.isUndefined(address)) { + contractAddress = address; + } else if (!_.isUndefined(artifactNetworkConfigs)) { + contractAddress = artifactNetworkConfigs.address; + } + + if (!_.isUndefined(contractAddress)) { + const doesContractExist = await this.doesContractExistAtAddressAsync(contractAddress); + if (!doesContractExist) { + utils.consoleLog(`Contract does not exist: ${artifact.contract_name} at ${contractAddress}`); + throw new Error(BlockchainCallErrs.ContractDoesNotExist); + } + } + + try { + const contractInstance = _.isUndefined(address) ? await c.deployed() : await c.at(address); + return contractInstance; + } catch (err) { + const errMsg = `${err}`; + utils.consoleLog(`Notice: Error encountered: ${err} ${err.stack}`); + if (_.includes(errMsg, 'not been deployed to detected network')) { + throw new Error(BlockchainCallErrs.ContractDoesNotExist); + } else { + await errorReporter.reportAsync(err); + throw new Error(BlockchainCallErrs.UnhandledError); + } + } + } } // tslint:disable:max-file-line-count diff --git a/packages/website/ts/components/dialogs/blockchain_err_dialog.tsx b/packages/website/ts/components/dialogs/blockchain_err_dialog.tsx index e0f61a29b..f555ca6b1 100644 --- a/packages/website/ts/components/dialogs/blockchain_err_dialog.tsx +++ b/packages/website/ts/components/dialogs/blockchain_err_dialog.tsx @@ -9,150 +9,150 @@ import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; interface BlockchainErrDialogProps { - blockchain: Blockchain; - blockchainErr: BlockchainErrs; - isOpen: boolean; - userAddress: string; - toggleDialogFn: (isOpen: boolean) => void; - networkId: number; + blockchain: Blockchain; + blockchainErr: BlockchainErrs; + isOpen: boolean; + userAddress: string; + toggleDialogFn: (isOpen: boolean) => void; + networkId: number; } export class BlockchainErrDialog extends React.Component { - public render() { - const dialogActions = [ - , - ]; + public render() { + const dialogActions = [ + , + ]; - const hasWalletAddress = this.props.userAddress !== ''; - return ( - -
- {this._renderExplanation(hasWalletAddress)} -
-
- ); - } - private _getTitle(hasWalletAddress: boolean) { - if (this.props.blockchainErr === BlockchainErrs.AContractNotDeployedOnNetwork) { - return '0x smart contracts not found'; - } else if (!hasWalletAddress) { - return 'Enable wallet communication'; - } else if (this.props.blockchainErr === BlockchainErrs.DisconnectedFromEthereumNode) { - return 'Disconnected from Ethereum network'; - } else { - return 'Unexpected error'; - } - } - private _renderExplanation(hasWalletAddress: boolean) { - if (this.props.blockchainErr === BlockchainErrs.AContractNotDeployedOnNetwork) { - return this._renderContractsNotDeployedExplanation(); - } else if (!hasWalletAddress) { - return this._renderNoWalletFoundExplanation(); - } else if (this.props.blockchainErr === BlockchainErrs.DisconnectedFromEthereumNode) { - return this._renderDisconnectedFromNode(); - } else { - return this._renderUnexpectedErrorExplanation(); - } - } - private _renderDisconnectedFromNode() { - return ( -
- You were disconnected from the backing Ethereum node. If using{' '} - - Metamask - {' '} - or{' '} - - Mist - {' '} - try refreshing the page. If using a locally hosted Ethereum node, make sure it's still running. -
- ); - } - private _renderUnexpectedErrorExplanation() { - return
We encountered an unexpected error. Please try refreshing the page.
; - } - private _renderNoWalletFoundExplanation() { - return ( -
-
- We were unable to access an Ethereum wallet you control. In order to interact with the 0x portal - dApp, we need a way to interact with one of your Ethereum wallets. There are two easy ways you can - enable us to do that: -
-

1. Metamask chrome extension

-
- You can install the{' '} - - Metamask - {' '} - Chrome extension Ethereum wallet. Once installed and set up, refresh this page. -
- Note: If you already have Metamask installed, make sure it is - unlocked. -
-
-

Parity Signer

-
- The{' '} - - Parity Signer Chrome extension - {' '} - lets you connect to a locally running Parity node. Make sure you have started your local Parity node - with {configs.IS_MAINNET_ENABLED && '`parity ui` or'} `parity --chain kovan ui` in order to connect - to {configs.IS_MAINNET_ENABLED ? 'mainnet or Kovan respectively.' : 'Kovan.'} -
-
- Note: If you have done one of the above steps and are still seeing - this message, we might still be unable to retrieve an Ethereum address by calling - `web3.eth.accounts`. Make sure you have created at least one Ethereum address. -
-
- ); - } - private _renderContractsNotDeployedExplanation() { - return ( -
-
- The 0x smart contracts are not deployed on the Ethereum network you are currently connected to - (network Id: {this.props.networkId}). In order to use the 0x portal dApp, please connect to the{' '} - {constants.TESTNET_NAME} testnet (network Id: {constants.NETWORK_ID_TESTNET}) - {configs.IS_MAINNET_ENABLED - ? ` or ${constants.MAINNET_NAME} (network Id: ${constants.NETWORK_ID_MAINNET}).` - : `.`} -
-

Metamask

-
- If you are using{' '} - - Metamask - , you can switch networks in the top left corner of the extension popover. -
-

Parity Signer

-
- If using the{' '} - - Parity Signer Chrome extension - , make sure to start your local Parity node with{' '} - {configs.IS_MAINNET_ENABLED - ? '`parity ui` or `parity --chain Kovan ui` in order to connect to mainnet \ + const hasWalletAddress = this.props.userAddress !== ''; + return ( + +
+ {this._renderExplanation(hasWalletAddress)} +
+
+ ); + } + private _getTitle(hasWalletAddress: boolean) { + if (this.props.blockchainErr === BlockchainErrs.AContractNotDeployedOnNetwork) { + return '0x smart contracts not found'; + } else if (!hasWalletAddress) { + return 'Enable wallet communication'; + } else if (this.props.blockchainErr === BlockchainErrs.DisconnectedFromEthereumNode) { + return 'Disconnected from Ethereum network'; + } else { + return 'Unexpected error'; + } + } + private _renderExplanation(hasWalletAddress: boolean) { + if (this.props.blockchainErr === BlockchainErrs.AContractNotDeployedOnNetwork) { + return this._renderContractsNotDeployedExplanation(); + } else if (!hasWalletAddress) { + return this._renderNoWalletFoundExplanation(); + } else if (this.props.blockchainErr === BlockchainErrs.DisconnectedFromEthereumNode) { + return this._renderDisconnectedFromNode(); + } else { + return this._renderUnexpectedErrorExplanation(); + } + } + private _renderDisconnectedFromNode() { + return ( +
+ You were disconnected from the backing Ethereum node. If using{' '} + + Metamask + {' '} + or{' '} + + Mist + {' '} + try refreshing the page. If using a locally hosted Ethereum node, make sure it's still running. +
+ ); + } + private _renderUnexpectedErrorExplanation() { + return
We encountered an unexpected error. Please try refreshing the page.
; + } + private _renderNoWalletFoundExplanation() { + return ( +
+
+ We were unable to access an Ethereum wallet you control. In order to interact with the 0x portal + dApp, we need a way to interact with one of your Ethereum wallets. There are two easy ways you can + enable us to do that: +
+

1. Metamask chrome extension

+
+ You can install the{' '} + + Metamask + {' '} + Chrome extension Ethereum wallet. Once installed and set up, refresh this page. +
+ Note: If you already have Metamask installed, make sure it is + unlocked. +
+
+

Parity Signer

+
+ The{' '} + + Parity Signer Chrome extension + {' '} + lets you connect to a locally running Parity node. Make sure you have started your local Parity node + with {configs.IS_MAINNET_ENABLED && '`parity ui` or'} `parity --chain kovan ui` in order to connect + to {configs.IS_MAINNET_ENABLED ? 'mainnet or Kovan respectively.' : 'Kovan.'} +
+
+ Note: If you have done one of the above steps and are still seeing + this message, we might still be unable to retrieve an Ethereum address by calling + `web3.eth.accounts`. Make sure you have created at least one Ethereum address. +
+
+ ); + } + private _renderContractsNotDeployedExplanation() { + return ( +
+
+ The 0x smart contracts are not deployed on the Ethereum network you are currently connected to + (network Id: {this.props.networkId}). In order to use the 0x portal dApp, please connect to the{' '} + {constants.TESTNET_NAME} testnet (network Id: {constants.NETWORK_ID_TESTNET}) + {configs.IS_MAINNET_ENABLED + ? ` or ${constants.MAINNET_NAME} (network Id: ${constants.NETWORK_ID_MAINNET}).` + : `.`} +
+

Metamask

+
+ If you are using{' '} + + Metamask + , you can switch networks in the top left corner of the extension popover. +
+

Parity Signer

+
+ If using the{' '} + + Parity Signer Chrome extension + , make sure to start your local Parity node with{' '} + {configs.IS_MAINNET_ENABLED + ? '`parity ui` or `parity --chain Kovan ui` in order to connect to mainnet \ or Kovan respectively.' - : '`parity --chain kovan ui` in order to connect to Kovan.'} -
-
- ); - } + : '`parity --chain kovan ui` in order to connect to Kovan.'} +
+
+ ); + } } diff --git a/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx b/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx index 45ba5cc9e..661cc1d8c 100644 --- a/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx +++ b/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx @@ -8,156 +8,156 @@ import { Side, Token, TokenState } from 'ts/types'; import { colors } from 'ts/utils/colors'; interface EthWethConversionDialogProps { - direction: Side; - onComplete: (direction: Side, value: BigNumber) => void; - onCancelled: () => void; - isOpen: boolean; - token: Token; - tokenState: TokenState; - etherBalance: BigNumber; + direction: Side; + onComplete: (direction: Side, value: BigNumber) => void; + onCancelled: () => void; + isOpen: boolean; + token: Token; + tokenState: TokenState; + etherBalance: BigNumber; } interface EthWethConversionDialogState { - value?: BigNumber; - shouldShowIncompleteErrs: boolean; - hasErrors: boolean; + value?: BigNumber; + shouldShowIncompleteErrs: boolean; + hasErrors: boolean; } export class EthWethConversionDialog extends React.Component< - EthWethConversionDialogProps, - EthWethConversionDialogState + EthWethConversionDialogProps, + EthWethConversionDialogState > { - constructor() { - super(); - this.state = { - shouldShowIncompleteErrs: false, - hasErrors: false, - }; - } - public render() { - const convertDialogActions = [ - , - , - ]; - const title = this.props.direction === Side.Deposit ? 'Wrap ETH' : 'Unwrap WETH'; - return ( - - {this._renderConversionDialogBody()} - - ); - } - private _renderConversionDialogBody() { - const explanation = - this.props.direction === Side.Deposit - ? 'Convert your Ether into a tokenized, tradable form.' - : "Convert your Wrapped Ether back into it's native form."; - const isWrappedVersion = this.props.direction === Side.Receive; - return ( -
-
{explanation}
-
-
- {this._renderCurrency(isWrappedVersion)} -
- -
- {this._renderCurrency(!isWrappedVersion)} -
-
- {this.props.direction === Side.Receive ? ( - - ) : ( - - )} -
-
1 ETH = 1 WETH
- {this.props.direction === Side.Receive && ( -
- Max -
- )} -
-
-
-
- ); - } - private _renderCurrency(isWrappedVersion: boolean) { - const name = isWrappedVersion ? 'Wrapped Ether' : 'Ether'; - const iconUrl = isWrappedVersion ? '/images/token_icons/ether_erc20.png' : '/images/ether.png'; - const symbol = isWrappedVersion ? 'WETH' : 'ETH'; - return ( -
-
- {name} -
-
- -
-
- ({symbol}) -
-
- ); - } - private _onMaxClick() { - this.setState({ - value: this.props.tokenState.balance, - }); - } - private _onValueChange(isValid: boolean, amount?: BigNumber) { - this.setState({ - value: amount, - hasErrors: !isValid, - }); - } - private _onConvertClick() { - if (this.state.hasErrors) { - this.setState({ - shouldShowIncompleteErrs: true, - }); - } else { - const value = this.state.value; - this.setState({ - value: undefined, - }); - this.props.onComplete(this.props.direction, value); - } - } - private _onCancel() { - this.setState({ - value: undefined, - }); - this.props.onCancelled(); - } + constructor() { + super(); + this.state = { + shouldShowIncompleteErrs: false, + hasErrors: false, + }; + } + public render() { + const convertDialogActions = [ + , + , + ]; + const title = this.props.direction === Side.Deposit ? 'Wrap ETH' : 'Unwrap WETH'; + return ( + + {this._renderConversionDialogBody()} + + ); + } + private _renderConversionDialogBody() { + const explanation = + this.props.direction === Side.Deposit + ? 'Convert your Ether into a tokenized, tradable form.' + : "Convert your Wrapped Ether back into it's native form."; + const isWrappedVersion = this.props.direction === Side.Receive; + return ( +
+
{explanation}
+
+
+ {this._renderCurrency(isWrappedVersion)} +
+ +
+ {this._renderCurrency(!isWrappedVersion)} +
+
+ {this.props.direction === Side.Receive ? ( + + ) : ( + + )} +
+
1 ETH = 1 WETH
+ {this.props.direction === Side.Receive && ( +
+ Max +
+ )} +
+
+
+
+ ); + } + private _renderCurrency(isWrappedVersion: boolean) { + const name = isWrappedVersion ? 'Wrapped Ether' : 'Ether'; + const iconUrl = isWrappedVersion ? '/images/token_icons/ether_erc20.png' : '/images/ether.png'; + const symbol = isWrappedVersion ? 'WETH' : 'ETH'; + return ( +
+
+ {name} +
+
+ +
+
+ ({symbol}) +
+
+ ); + } + private _onMaxClick() { + this.setState({ + value: this.props.tokenState.balance, + }); + } + private _onValueChange(isValid: boolean, amount?: BigNumber) { + this.setState({ + value: amount, + hasErrors: !isValid, + }); + } + private _onConvertClick() { + if (this.state.hasErrors) { + this.setState({ + shouldShowIncompleteErrs: true, + }); + } else { + const value = this.state.value; + this.setState({ + value: undefined, + }); + this.props.onComplete(this.props.direction, value); + } + } + private _onCancel() { + this.setState({ + value: undefined, + }); + this.props.onCancelled(); + } } diff --git a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx index 8b7760a1a..60db93c52 100644 --- a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx +++ b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx @@ -17,245 +17,245 @@ import { utils } from 'ts/utils/utils'; const VALID_ETHEREUM_DERIVATION_PATH_PREFIX = `44'/60'`; enum LedgerSteps { - CONNECT, - SELECT_ADDRESS, + CONNECT, + SELECT_ADDRESS, } interface LedgerConfigDialogProps { - isOpen: boolean; - toggleDialogFn: (isOpen: boolean) => void; - dispatcher: Dispatcher; - blockchain: Blockchain; - networkId: number; + isOpen: boolean; + toggleDialogFn: (isOpen: boolean) => void; + dispatcher: Dispatcher; + blockchain: Blockchain; + networkId: number; } interface LedgerConfigDialogState { - didConnectFail: boolean; - stepIndex: LedgerSteps; - userAddresses: string[]; - addressBalances: BigNumber[]; - derivationPath: string; - derivationErrMsg: string; + didConnectFail: boolean; + stepIndex: LedgerSteps; + userAddresses: string[]; + addressBalances: BigNumber[]; + derivationPath: string; + derivationErrMsg: string; } export class LedgerConfigDialog extends React.Component { - constructor(props: LedgerConfigDialogProps) { - super(props); - this.state = { - didConnectFail: false, - stepIndex: LedgerSteps.CONNECT, - userAddresses: [], - addressBalances: [], - derivationPath: configs.DEFAULT_DERIVATION_PATH, - derivationErrMsg: '', - }; - } - public render() { - const dialogActions = [ - , - ]; - const dialogTitle = - this.state.stepIndex === LedgerSteps.CONNECT ? 'Connect to your Ledger' : 'Select desired address'; - return ( - -
- {this.state.stepIndex === LedgerSteps.CONNECT && this._renderConnectStep()} - {this.state.stepIndex === LedgerSteps.SELECT_ADDRESS && this._renderSelectAddressStep()} -
-
- ); - } - private _renderConnectStep() { - return ( -
-
Follow these instructions before proceeding:
-
    -
  1. Connect your Ledger Nano S & Open the Ethereum application
  2. -
  3. Verify that Browser Support is enabled in Settings
  4. -
  5. - If no Browser Support is found in settings, verify that you have{' '} - - Firmware >1.2 - -
  6. -
-
- - {this.state.didConnectFail && ( -
- Failed to connect. Follow the instructions and try again. -
- )} -
-
- ); - } - private _renderSelectAddressStep() { - return ( -
-
- - - - Address - Balance - - - {this._renderAddressTableRows()} -
-
-
-
- -
-
- -
-
-
- ); - } - private _renderAddressTableRows() { - const rows = _.map(this.state.userAddresses, (userAddress: string, i: number) => { - const balance = this.state.addressBalances[i]; - const addressTooltipId = `address-${userAddress}`; - const balanceTooltipId = `balance-${userAddress}`; - const networkName = constants.NETWORK_NAME_BY_ID[this.props.networkId]; - // We specifically prefix kovan ETH. - // TODO: We should probably add prefixes for all networks - const isKovanNetwork = networkName === 'Kovan'; - const balanceString = `${balance.toString()} ${isKovanNetwork ? 'Kovan ' : ''}ETH`; - return ( - - -
- {userAddress} -
- {userAddress} -
- -
- {balanceString} -
- {balanceString} -
-
- ); - }); - return rows; - } - private _onClose() { - this.setState({ - didConnectFail: false, - }); - const isOpen = false; - this.props.toggleDialogFn(isOpen); - } - private _onAddressSelected(selectedRowIndexes: number[]) { - const selectedRowIndex = selectedRowIndexes[0]; - this.props.blockchain.updateLedgerDerivationIndex(selectedRowIndex); - const selectedAddress = this.state.userAddresses[selectedRowIndex]; - const selectAddressBalance = this.state.addressBalances[selectedRowIndex]; - this.props.dispatcher.updateUserAddress(selectedAddress); - this.props.blockchain.updateWeb3WrapperPrevUserAddress(selectedAddress); - this.props.dispatcher.updateUserEtherBalance(selectAddressBalance); - this.setState({ - stepIndex: LedgerSteps.CONNECT, - }); - const isOpen = false; - this.props.toggleDialogFn(isOpen); - } - private async _onFetchAddressesForDerivationPathAsync(): Promise { - const currentlySetPath = this.props.blockchain.getLedgerDerivationPathIfExists(); - let didSucceed; - if (currentlySetPath === this.state.derivationPath) { - didSucceed = true; - return didSucceed; - } - this.props.blockchain.updateLedgerDerivationPathIfExists(this.state.derivationPath); - didSucceed = await this._fetchAddressesAndBalancesAsync(); - if (!didSucceed) { - this.setState({ - derivationErrMsg: 'Failed to connect to Ledger.', - }); - } - return didSucceed; - } - private async _fetchAddressesAndBalancesAsync() { - let userAddresses: string[]; - const addressBalances: BigNumber[] = []; - try { - userAddresses = await this._getUserAddressesAsync(); - for (const address of userAddresses) { - const balance = await this.props.blockchain.getBalanceInEthAsync(address); - addressBalances.push(balance); - } - } catch (err) { - utils.consoleLog(`Ledger error: ${JSON.stringify(err)}`); - this.setState({ - didConnectFail: true, - }); - return false; - } - this.setState({ - userAddresses, - addressBalances, - }); - return true; - } - private _onDerivationPathChanged(e: any, derivationPath: string) { - let derivationErrMsg = ''; - if (!_.startsWith(derivationPath, VALID_ETHEREUM_DERIVATION_PATH_PREFIX)) { - derivationErrMsg = 'Must be valid Ethereum path.'; - } + constructor(props: LedgerConfigDialogProps) { + super(props); + this.state = { + didConnectFail: false, + stepIndex: LedgerSteps.CONNECT, + userAddresses: [], + addressBalances: [], + derivationPath: configs.DEFAULT_DERIVATION_PATH, + derivationErrMsg: '', + }; + } + public render() { + const dialogActions = [ + , + ]; + const dialogTitle = + this.state.stepIndex === LedgerSteps.CONNECT ? 'Connect to your Ledger' : 'Select desired address'; + return ( + +
+ {this.state.stepIndex === LedgerSteps.CONNECT && this._renderConnectStep()} + {this.state.stepIndex === LedgerSteps.SELECT_ADDRESS && this._renderSelectAddressStep()} +
+
+ ); + } + private _renderConnectStep() { + return ( +
+
Follow these instructions before proceeding:
+
    +
  1. Connect your Ledger Nano S & Open the Ethereum application
  2. +
  3. Verify that Browser Support is enabled in Settings
  4. +
  5. + If no Browser Support is found in settings, verify that you have{' '} + + Firmware >1.2 + +
  6. +
+
+ + {this.state.didConnectFail && ( +
+ Failed to connect. Follow the instructions and try again. +
+ )} +
+
+ ); + } + private _renderSelectAddressStep() { + return ( +
+
+ + + + Address + Balance + + + {this._renderAddressTableRows()} +
+
+
+
+ +
+
+ +
+
+
+ ); + } + private _renderAddressTableRows() { + const rows = _.map(this.state.userAddresses, (userAddress: string, i: number) => { + const balance = this.state.addressBalances[i]; + const addressTooltipId = `address-${userAddress}`; + const balanceTooltipId = `balance-${userAddress}`; + const networkName = constants.NETWORK_NAME_BY_ID[this.props.networkId]; + // We specifically prefix kovan ETH. + // TODO: We should probably add prefixes for all networks + const isKovanNetwork = networkName === 'Kovan'; + const balanceString = `${balance.toString()} ${isKovanNetwork ? 'Kovan ' : ''}ETH`; + return ( + + +
+ {userAddress} +
+ {userAddress} +
+ +
+ {balanceString} +
+ {balanceString} +
+
+ ); + }); + return rows; + } + private _onClose() { + this.setState({ + didConnectFail: false, + }); + const isOpen = false; + this.props.toggleDialogFn(isOpen); + } + private _onAddressSelected(selectedRowIndexes: number[]) { + const selectedRowIndex = selectedRowIndexes[0]; + this.props.blockchain.updateLedgerDerivationIndex(selectedRowIndex); + const selectedAddress = this.state.userAddresses[selectedRowIndex]; + const selectAddressBalance = this.state.addressBalances[selectedRowIndex]; + this.props.dispatcher.updateUserAddress(selectedAddress); + this.props.blockchain.updateWeb3WrapperPrevUserAddress(selectedAddress); + this.props.dispatcher.updateUserEtherBalance(selectAddressBalance); + this.setState({ + stepIndex: LedgerSteps.CONNECT, + }); + const isOpen = false; + this.props.toggleDialogFn(isOpen); + } + private async _onFetchAddressesForDerivationPathAsync(): Promise { + const currentlySetPath = this.props.blockchain.getLedgerDerivationPathIfExists(); + let didSucceed; + if (currentlySetPath === this.state.derivationPath) { + didSucceed = true; + return didSucceed; + } + this.props.blockchain.updateLedgerDerivationPathIfExists(this.state.derivationPath); + didSucceed = await this._fetchAddressesAndBalancesAsync(); + if (!didSucceed) { + this.setState({ + derivationErrMsg: 'Failed to connect to Ledger.', + }); + } + return didSucceed; + } + private async _fetchAddressesAndBalancesAsync() { + let userAddresses: string[]; + const addressBalances: BigNumber[] = []; + try { + userAddresses = await this._getUserAddressesAsync(); + for (const address of userAddresses) { + const balance = await this.props.blockchain.getBalanceInEthAsync(address); + addressBalances.push(balance); + } + } catch (err) { + utils.consoleLog(`Ledger error: ${JSON.stringify(err)}`); + this.setState({ + didConnectFail: true, + }); + return false; + } + this.setState({ + userAddresses, + addressBalances, + }); + return true; + } + private _onDerivationPathChanged(e: any, derivationPath: string) { + let derivationErrMsg = ''; + if (!_.startsWith(derivationPath, VALID_ETHEREUM_DERIVATION_PATH_PREFIX)) { + derivationErrMsg = 'Must be valid Ethereum path.'; + } - this.setState({ - derivationPath, - derivationErrMsg, - }); - } - private async _onConnectLedgerClickAsync() { - const didSucceed = await this._fetchAddressesAndBalancesAsync(); - if (didSucceed) { - this.setState({ - stepIndex: LedgerSteps.SELECT_ADDRESS, - }); - } - return didSucceed; - } - private async _getUserAddressesAsync(): Promise { - let userAddresses: string[]; - userAddresses = await this.props.blockchain.getUserAccountsAsync(); + this.setState({ + derivationPath, + derivationErrMsg, + }); + } + private async _onConnectLedgerClickAsync() { + const didSucceed = await this._fetchAddressesAndBalancesAsync(); + if (didSucceed) { + this.setState({ + stepIndex: LedgerSteps.SELECT_ADDRESS, + }); + } + return didSucceed; + } + private async _getUserAddressesAsync(): Promise { + let userAddresses: string[]; + userAddresses = await this.props.blockchain.getUserAccountsAsync(); - if (_.isEmpty(userAddresses)) { - throw new Error('No addresses retrieved.'); - } - return userAddresses; - } + if (_.isEmpty(userAddresses)) { + throw new Error('No addresses retrieved.'); + } + return userAddresses; + } } diff --git a/packages/website/ts/components/dialogs/portal_disclaimer_dialog.tsx b/packages/website/ts/components/dialogs/portal_disclaimer_dialog.tsx index 1c5efc978..3ecc454a0 100644 --- a/packages/website/ts/components/dialogs/portal_disclaimer_dialog.tsx +++ b/packages/website/ts/components/dialogs/portal_disclaimer_dialog.tsx @@ -4,33 +4,33 @@ import * as React from 'react'; import { colors } from 'ts/utils/colors'; interface PortalDisclaimerDialogProps { - isOpen: boolean; - onToggleDialog: () => void; + isOpen: boolean; + onToggleDialog: () => void; } export function PortalDisclaimerDialog(props: PortalDisclaimerDialogProps) { - return ( - ]} - open={props.isOpen} - onRequestClose={props.onToggleDialog} - autoScrollBodyContent={true} - modal={true} - > -
-
- 0x Portal is a free software-based tool intended to help users to buy and sell ERC20-compatible - blockchain tokens through the 0x protocol on a purely peer-to-peer basis. 0x portal is not a - regulated marketplace, exchange or intermediary of any kind, and therefore, you should only use 0x - portal to exchange tokens that are not securities, commodity interests, or any other form of - regulated instrument. 0x has not attempted to screen or otherwise limit the tokens that you may - enter in 0x Portal. By clicking “I Agree” below, you understand that you are solely responsible for - using 0x Portal and buying and selling tokens using 0x Portal in compliance with all applicable laws - and regulations. -
-
-
- ); + return ( + ]} + open={props.isOpen} + onRequestClose={props.onToggleDialog} + autoScrollBodyContent={true} + modal={true} + > +
+
+ 0x Portal is a free software-based tool intended to help users to buy and sell ERC20-compatible + blockchain tokens through the 0x protocol on a purely peer-to-peer basis. 0x portal is not a + regulated marketplace, exchange or intermediary of any kind, and therefore, you should only use 0x + portal to exchange tokens that are not securities, commodity interests, or any other form of + regulated instrument. 0x has not attempted to screen or otherwise limit the tokens that you may + enter in 0x Portal. By clicking “I Agree” below, you understand that you are solely responsible for + using 0x Portal and buying and selling tokens using 0x Portal in compliance with all applicable laws + and regulations. +
+
+
+ ); } diff --git a/packages/website/ts/components/dialogs/send_dialog.tsx b/packages/website/ts/components/dialogs/send_dialog.tsx index b9022cd9b..b3dbce598 100644 --- a/packages/website/ts/components/dialogs/send_dialog.tsx +++ b/packages/website/ts/components/dialogs/send_dialog.tsx @@ -8,110 +8,110 @@ import { TokenAmountInput } from 'ts/components/inputs/token_amount_input'; import { Token, TokenState } from 'ts/types'; interface SendDialogProps { - onComplete: (recipient: string, value: BigNumber) => void; - onCancelled: () => void; - isOpen: boolean; - token: Token; - tokenState: TokenState; + onComplete: (recipient: string, value: BigNumber) => void; + onCancelled: () => void; + isOpen: boolean; + token: Token; + tokenState: TokenState; } interface SendDialogState { - value?: BigNumber; - recipient: string; - shouldShowIncompleteErrs: boolean; - isAmountValid: boolean; + value?: BigNumber; + recipient: string; + shouldShowIncompleteErrs: boolean; + isAmountValid: boolean; } export class SendDialog extends React.Component { - constructor() { - super(); - this.state = { - recipient: '', - shouldShowIncompleteErrs: false, - isAmountValid: false, - }; - } - public render() { - const transferDialogActions = [ - , - , - ]; - return ( - - {this._renderSendDialogBody()} - - ); - } - private _renderSendDialogBody() { - return ( -
-
- -
- -
- ); - } - private _onRecipientChange(recipient?: string) { - this.setState({ - shouldShowIncompleteErrs: false, - recipient, - }); - } - private _onValueChange(isValid: boolean, amount?: BigNumber) { - this.setState({ - isAmountValid: isValid, - value: amount, - }); - } - private _onSendClick() { - if (this._hasErrors()) { - this.setState({ - shouldShowIncompleteErrs: true, - }); - } else { - const value = this.state.value; - this.setState({ - recipient: undefined, - value: undefined, - }); - this.props.onComplete(this.state.recipient, value); - } - } - private _onCancel() { - this.setState({ - value: undefined, - }); - this.props.onCancelled(); - } - private _hasErrors() { - return _.isUndefined(this.state.recipient) || _.isUndefined(this.state.value) || !this.state.isAmountValid; - } + constructor() { + super(); + this.state = { + recipient: '', + shouldShowIncompleteErrs: false, + isAmountValid: false, + }; + } + public render() { + const transferDialogActions = [ + , + , + ]; + return ( + + {this._renderSendDialogBody()} + + ); + } + private _renderSendDialogBody() { + return ( +
+
+ +
+ +
+ ); + } + private _onRecipientChange(recipient?: string) { + this.setState({ + shouldShowIncompleteErrs: false, + recipient, + }); + } + private _onValueChange(isValid: boolean, amount?: BigNumber) { + this.setState({ + isAmountValid: isValid, + value: amount, + }); + } + private _onSendClick() { + if (this._hasErrors()) { + this.setState({ + shouldShowIncompleteErrs: true, + }); + } else { + const value = this.state.value; + this.setState({ + recipient: undefined, + value: undefined, + }); + this.props.onComplete(this.state.recipient, value); + } + } + private _onCancel() { + this.setState({ + value: undefined, + }); + this.props.onCancelled(); + } + private _hasErrors() { + return _.isUndefined(this.state.recipient) || _.isUndefined(this.state.value) || !this.state.isAmountValid; + } } diff --git a/packages/website/ts/components/dialogs/track_token_confirmation_dialog.tsx b/packages/website/ts/components/dialogs/track_token_confirmation_dialog.tsx index b1804e95c..3f29d46f8 100644 --- a/packages/website/ts/components/dialogs/track_token_confirmation_dialog.tsx +++ b/packages/website/ts/components/dialogs/track_token_confirmation_dialog.tsx @@ -9,94 +9,94 @@ import { Dispatcher } from 'ts/redux/dispatcher'; import { Token, TokenByAddress } from 'ts/types'; interface TrackTokenConfirmationDialogProps { - tokens: Token[]; - tokenByAddress: TokenByAddress; - isOpen: boolean; - onToggleDialog: (didConfirmTokenTracking: boolean) => void; - dispatcher: Dispatcher; - networkId: number; - blockchain: Blockchain; - userAddress: string; + tokens: Token[]; + tokenByAddress: TokenByAddress; + isOpen: boolean; + onToggleDialog: (didConfirmTokenTracking: boolean) => void; + dispatcher: Dispatcher; + networkId: number; + blockchain: Blockchain; + userAddress: string; } interface TrackTokenConfirmationDialogState { - isAddingTokenToTracked: boolean; + isAddingTokenToTracked: boolean; } export class TrackTokenConfirmationDialog extends React.Component< - TrackTokenConfirmationDialogProps, - TrackTokenConfirmationDialogState + TrackTokenConfirmationDialogProps, + TrackTokenConfirmationDialogState > { - constructor(props: TrackTokenConfirmationDialogProps) { - super(props); - this.state = { - isAddingTokenToTracked: false, - }; - } - public render() { - const tokens = this.props.tokens; - return ( - , - , - ]} - open={this.props.isOpen} - onRequestClose={this.props.onToggleDialog.bind(this, false)} - autoScrollBodyContent={true} - > -
- -
-
- ); - } - private async _onTrackConfirmationRespondedAsync(didUserAcceptTracking: boolean) { - if (!didUserAcceptTracking) { - this.props.onToggleDialog(didUserAcceptTracking); - return; - } - this.setState({ - isAddingTokenToTracked: true, - }); - for (const token of this.props.tokens) { - const newTokenEntry = { - ...token, - }; + constructor(props: TrackTokenConfirmationDialogProps) { + super(props); + this.state = { + isAddingTokenToTracked: false, + }; + } + public render() { + const tokens = this.props.tokens; + return ( + , + , + ]} + open={this.props.isOpen} + onRequestClose={this.props.onToggleDialog.bind(this, false)} + autoScrollBodyContent={true} + > +
+ +
+
+ ); + } + private async _onTrackConfirmationRespondedAsync(didUserAcceptTracking: boolean) { + if (!didUserAcceptTracking) { + this.props.onToggleDialog(didUserAcceptTracking); + return; + } + this.setState({ + isAddingTokenToTracked: true, + }); + for (const token of this.props.tokens) { + const newTokenEntry = { + ...token, + }; - newTokenEntry.isTracked = true; - trackedTokenStorage.addTrackedTokenToUser(this.props.userAddress, this.props.networkId, newTokenEntry); - this.props.dispatcher.updateTokenByAddress([newTokenEntry]); + newTokenEntry.isTracked = true; + trackedTokenStorage.addTrackedTokenToUser(this.props.userAddress, this.props.networkId, newTokenEntry); + this.props.dispatcher.updateTokenByAddress([newTokenEntry]); - const [balance, allowance] = await this.props.blockchain.getCurrentUserTokenBalanceAndAllowanceAsync( - token.address, - ); - this.props.dispatcher.updateTokenStateByAddress({ - [token.address]: { - balance, - allowance, - }, - }); - } + const [balance, allowance] = await this.props.blockchain.getCurrentUserTokenBalanceAndAllowanceAsync( + token.address, + ); + this.props.dispatcher.updateTokenStateByAddress({ + [token.address]: { + balance, + allowance, + }, + }); + } - this.setState({ - isAddingTokenToTracked: false, - }); - this.props.onToggleDialog(didUserAcceptTracking); - } + this.setState({ + isAddingTokenToTracked: false, + }); + this.props.onToggleDialog(didUserAcceptTracking); + } } diff --git a/packages/website/ts/components/dialogs/u2f_not_supported_dialog.tsx b/packages/website/ts/components/dialogs/u2f_not_supported_dialog.tsx index 2ea51d07b..098e3e26d 100644 --- a/packages/website/ts/components/dialogs/u2f_not_supported_dialog.tsx +++ b/packages/website/ts/components/dialogs/u2f_not_supported_dialog.tsx @@ -5,42 +5,42 @@ import { colors } from 'ts/utils/colors'; import { constants } from 'ts/utils/constants'; interface U2fNotSupportedDialogProps { - isOpen: boolean; - onToggleDialog: () => void; + isOpen: boolean; + onToggleDialog: () => void; } export function U2fNotSupportedDialog(props: U2fNotSupportedDialogProps) { - return ( - ]} - open={props.isOpen} - onRequestClose={props.onToggleDialog.bind(this)} - autoScrollBodyContent={true} - > -
-
- It looks like your browser does not support U2F connections required for us to communicate with your - hardware wallet. Please use a browser that supports U2F connections and try again. -
-
-
    -
  • Chrome version 38 or later
  • -
  • Opera version 40 of later
  • -
  • - Firefox with{' '} - - this extension - . -
  • -
-
-
-
- ); + return ( + ]} + open={props.isOpen} + onRequestClose={props.onToggleDialog.bind(this)} + autoScrollBodyContent={true} + > +
+
+ It looks like your browser does not support U2F connections required for us to communicate with your + hardware wallet. Please use a browser that supports U2F connections and try again. +
+
+
    +
  • Chrome version 38 or later
  • +
  • Opera version 40 of later
  • +
  • + Firefox with{' '} + + this extension + . +
  • +
+
+
+
+ ); } diff --git a/packages/website/ts/components/dialogs/wrapped_eth_section_notice_dialog.tsx b/packages/website/ts/components/dialogs/wrapped_eth_section_notice_dialog.tsx index 98436eb50..9e91ff12d 100644 --- a/packages/website/ts/components/dialogs/wrapped_eth_section_notice_dialog.tsx +++ b/packages/website/ts/components/dialogs/wrapped_eth_section_notice_dialog.tsx @@ -4,30 +4,30 @@ import { colors } from 'material-ui/styles'; import * as React from 'react'; interface WrappedEthSectionNoticeDialogProps { - isOpen: boolean; - onToggleDialog: () => void; + isOpen: boolean; + onToggleDialog: () => void; } export function WrappedEthSectionNoticeDialog(props: WrappedEthSectionNoticeDialogProps) { - return ( - , - ]} - open={props.isOpen} - onRequestClose={props.onToggleDialog} - autoScrollBodyContent={true} - modal={true} - > -
-
- We have recently updated the Wrapped Ether token (WETH) used by 0x Portal. Don't worry, unwrapping - Ether tied to the old Wrapped Ether token can be done at any time by clicking on the "Wrap ETH" - section in the menu to the left. -
-
-
- ); + return ( + , + ]} + open={props.isOpen} + onRequestClose={props.onToggleDialog} + autoScrollBodyContent={true} + modal={true} + > +
+
+ We have recently updated the Wrapped Ether token (WETH) used by 0x Portal. Don't worry, unwrapping + Ether tied to the old Wrapped Ether token can be done at any time by clicking on the "Wrap ETH" + section in the menu to the left. +
+
+
+ ); } diff --git a/packages/website/ts/components/eth_weth_conversion_button.tsx b/packages/website/ts/components/eth_weth_conversion_button.tsx index af1b33eef..300e71f1f 100644 --- a/packages/website/ts/components/eth_weth_conversion_button.tsx +++ b/packages/website/ts/components/eth_weth_conversion_button.tsx @@ -12,115 +12,115 @@ import { errorReporter } from 'ts/utils/error_reporter'; import { utils } from 'ts/utils/utils'; interface EthWethConversionButtonProps { - direction: Side; - ethToken: Token; - ethTokenState: TokenState; - dispatcher: Dispatcher; - blockchain: Blockchain; - userEtherBalance: BigNumber; - isOutdatedWrappedEther: boolean; - onConversionSuccessful?: () => void; - isDisabled?: boolean; + direction: Side; + ethToken: Token; + ethTokenState: TokenState; + dispatcher: Dispatcher; + blockchain: Blockchain; + userEtherBalance: BigNumber; + isOutdatedWrappedEther: boolean; + onConversionSuccessful?: () => void; + isDisabled?: boolean; } interface EthWethConversionButtonState { - isEthConversionDialogVisible: boolean; - isEthConversionHappening: boolean; + isEthConversionDialogVisible: boolean; + isEthConversionHappening: boolean; } export class EthWethConversionButton extends React.Component< - EthWethConversionButtonProps, - EthWethConversionButtonState + EthWethConversionButtonProps, + EthWethConversionButtonState > { - public static defaultProps: Partial = { - isDisabled: false, - onConversionSuccessful: _.noop, - }; - public constructor(props: EthWethConversionButtonProps) { - super(props); - this.state = { - isEthConversionDialogVisible: false, - isEthConversionHappening: false, - }; - } - public render() { - const labelStyle = this.state.isEthConversionHappening ? { fontSize: 10 } : {}; - let callToActionLabel; - let inProgressLabel; - if (this.props.direction === Side.Deposit) { - callToActionLabel = 'Wrap'; - inProgressLabel = 'Wrapping...'; - } else { - callToActionLabel = 'Unwrap'; - inProgressLabel = 'Unwrapping...'; - } - return ( -
- - -
- ); - } - private _toggleConversionDialog() { - this.setState({ - isEthConversionDialogVisible: !this.state.isEthConversionDialogVisible, - }); - } - private async _onConversionAmountSelectedAsync(direction: Side, value: BigNumber) { - this.setState({ - isEthConversionHappening: true, - }); - this._toggleConversionDialog(); - const token = this.props.ethToken; - const tokenState = this.props.ethTokenState; - let balance = tokenState.balance; - try { - if (direction === Side.Deposit) { - await this.props.blockchain.convertEthToWrappedEthTokensAsync(token.address, value); - const ethAmount = ZeroEx.toUnitAmount(value, constants.DECIMAL_PLACES_ETH); - this.props.dispatcher.showFlashMessage(`Successfully wrapped ${ethAmount.toString()} ETH to WETH`); - balance = balance.plus(value); - } else { - await this.props.blockchain.convertWrappedEthTokensToEthAsync(token.address, value); - const tokenAmount = ZeroEx.toUnitAmount(value, token.decimals); - this.props.dispatcher.showFlashMessage(`Successfully unwrapped ${tokenAmount.toString()} WETH to ETH`); - balance = balance.minus(value); - } - if (!this.props.isOutdatedWrappedEther) { - this.props.dispatcher.replaceTokenBalanceByAddress(token.address, balance); - } - this.props.onConversionSuccessful(); - } catch (err) { - const errMsg = `${err}`; - if (_.includes(errMsg, BlockchainCallErrs.UserHasNoAssociatedAddresses)) { - this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); - } else if (!_.includes(errMsg, 'User denied transaction')) { - utils.consoleLog(`Unexpected error encountered: ${err}`); - utils.consoleLog(err.stack); - const errorMsg = - direction === Side.Deposit - ? 'Failed to wrap your ETH. Please try again.' - : 'Failed to unwrap your WETH. Please try again.'; - this.props.dispatcher.showFlashMessage(errorMsg); - await errorReporter.reportAsync(err); - } - } - this.setState({ - isEthConversionHappening: false, - }); - } + public static defaultProps: Partial = { + isDisabled: false, + onConversionSuccessful: _.noop, + }; + public constructor(props: EthWethConversionButtonProps) { + super(props); + this.state = { + isEthConversionDialogVisible: false, + isEthConversionHappening: false, + }; + } + public render() { + const labelStyle = this.state.isEthConversionHappening ? { fontSize: 10 } : {}; + let callToActionLabel; + let inProgressLabel; + if (this.props.direction === Side.Deposit) { + callToActionLabel = 'Wrap'; + inProgressLabel = 'Wrapping...'; + } else { + callToActionLabel = 'Unwrap'; + inProgressLabel = 'Unwrapping...'; + } + return ( +
+ + +
+ ); + } + private _toggleConversionDialog() { + this.setState({ + isEthConversionDialogVisible: !this.state.isEthConversionDialogVisible, + }); + } + private async _onConversionAmountSelectedAsync(direction: Side, value: BigNumber) { + this.setState({ + isEthConversionHappening: true, + }); + this._toggleConversionDialog(); + const token = this.props.ethToken; + const tokenState = this.props.ethTokenState; + let balance = tokenState.balance; + try { + if (direction === Side.Deposit) { + await this.props.blockchain.convertEthToWrappedEthTokensAsync(token.address, value); + const ethAmount = ZeroEx.toUnitAmount(value, constants.DECIMAL_PLACES_ETH); + this.props.dispatcher.showFlashMessage(`Successfully wrapped ${ethAmount.toString()} ETH to WETH`); + balance = balance.plus(value); + } else { + await this.props.blockchain.convertWrappedEthTokensToEthAsync(token.address, value); + const tokenAmount = ZeroEx.toUnitAmount(value, token.decimals); + this.props.dispatcher.showFlashMessage(`Successfully unwrapped ${tokenAmount.toString()} WETH to ETH`); + balance = balance.minus(value); + } + if (!this.props.isOutdatedWrappedEther) { + this.props.dispatcher.replaceTokenBalanceByAddress(token.address, balance); + } + this.props.onConversionSuccessful(); + } catch (err) { + const errMsg = `${err}`; + if (_.includes(errMsg, BlockchainCallErrs.UserHasNoAssociatedAddresses)) { + this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); + } else if (!_.includes(errMsg, 'User denied transaction')) { + utils.consoleLog(`Unexpected error encountered: ${err}`); + utils.consoleLog(err.stack); + const errorMsg = + direction === Side.Deposit + ? 'Failed to wrap your ETH. Please try again.' + : 'Failed to unwrap your WETH. Please try again.'; + this.props.dispatcher.showFlashMessage(errorMsg); + await errorReporter.reportAsync(err); + } + } + this.setState({ + isEthConversionHappening: false, + }); + } } diff --git a/packages/website/ts/components/eth_wrappers.tsx b/packages/website/ts/components/eth_wrappers.tsx index 1593d51f0..d074ec787 100644 --- a/packages/website/ts/components/eth_wrappers.tsx +++ b/packages/website/ts/components/eth_wrappers.tsx @@ -10,13 +10,13 @@ import { Blockchain } from 'ts/blockchain'; import { EthWethConversionButton } from 'ts/components/eth_weth_conversion_button'; import { Dispatcher } from 'ts/redux/dispatcher'; import { - EtherscanLinkSuffixes, - OutdatedWrappedEtherByNetworkId, - Side, - Token, - TokenByAddress, - TokenState, - TokenStateByAddress, + EtherscanLinkSuffixes, + OutdatedWrappedEtherByNetworkId, + Side, + Token, + TokenByAddress, + TokenState, + TokenStateByAddress, } from 'ts/types'; import { colors } from 'ts/utils/colors'; import { configs } from 'ts/utils/configs'; @@ -30,345 +30,345 @@ const ETHER_ICON_PATH = '/images/ether.png'; const OUTDATED_WETH_ICON_PATH = '/images/wrapped_eth_gray.png'; interface OutdatedWETHAddressToIsStateLoaded { - [address: string]: boolean; + [address: string]: boolean; } interface OutdatedWETHStateByAddress { - [address: string]: TokenState; + [address: string]: TokenState; } interface EthWrappersProps { - networkId: number; - blockchain: Blockchain; - dispatcher: Dispatcher; - tokenByAddress: TokenByAddress; - tokenStateByAddress: TokenStateByAddress; - userAddress: string; - userEtherBalance: BigNumber; + networkId: number; + blockchain: Blockchain; + dispatcher: Dispatcher; + tokenByAddress: TokenByAddress; + tokenStateByAddress: TokenStateByAddress; + userAddress: string; + userEtherBalance: BigNumber; } interface EthWrappersState { - outdatedWETHAddressToIsStateLoaded: OutdatedWETHAddressToIsStateLoaded; - outdatedWETHStateByAddress: OutdatedWETHStateByAddress; + outdatedWETHAddressToIsStateLoaded: OutdatedWETHAddressToIsStateLoaded; + outdatedWETHStateByAddress: OutdatedWETHStateByAddress; } export class EthWrappers extends React.Component { - constructor(props: EthWrappersProps) { - super(props); - const outdatedWETHAddresses = this._getOutdatedWETHAddresses(); - const outdatedWETHAddressToIsStateLoaded: OutdatedWETHAddressToIsStateLoaded = {}; - const outdatedWETHStateByAddress: OutdatedWETHStateByAddress = {}; - _.each(outdatedWETHAddresses, outdatedWETHAddress => { - outdatedWETHAddressToIsStateLoaded[outdatedWETHAddress] = false; - outdatedWETHStateByAddress[outdatedWETHAddress] = { - balance: new BigNumber(0), - allowance: new BigNumber(0), - }; - }); - this.state = { - outdatedWETHAddressToIsStateLoaded, - outdatedWETHStateByAddress, - }; - } - public componentDidMount() { - window.scrollTo(0, 0); - // tslint:disable-next-line:no-floating-promises - this._fetchOutdatedWETHStateAsync(); - } - public render() { - const tokens = _.values(this.props.tokenByAddress); - const etherToken = _.find(tokens, { symbol: 'WETH' }); - const etherTokenState = this.props.tokenStateByAddress[etherToken.address]; - const wethBalance = ZeroEx.toUnitAmount(etherTokenState.balance, constants.DECIMAL_PLACES_ETH); - const isBidirectional = true; - const etherscanUrl = utils.getEtherScanLinkIfExists( - etherToken.address, - this.props.networkId, - EtherscanLinkSuffixes.Address, - ); - const tokenLabel = this._renderToken('Wrapped Ether', etherToken.address, configs.ICON_URL_BY_SYMBOL.WETH); - return ( -
-
-

ETH Wrapper

- -
- -
-
Wrap ETH into an ERC20-compliant Ether token. 1 ETH = 1 WETH.
-
- - - - ETH Token - Balance - - {this._renderActionColumnTitle(isBidirectional)} - - - - - - -
- -
- ETH -
-
-
- - {this.props.userEtherBalance.toFixed(PRECISION)} ETH - - - - -
- - - {this._renderTokenLink(tokenLabel, etherscanUrl)} - - {wethBalance.toFixed(PRECISION)} WETH - - - - -
-
-
-
-
-

Outdated WETH

- -
- The{' '} - - canonical WETH - {' '} - contract is updated when necessary. Unwrap outdated WETH in order to
 retrieve your ETH and move - it to the updated WETH token. -
-
- - - - WETH Version - Balance - - {this._renderActionColumnTitle(!isBidirectional)} - - - - - {this._renderOutdatedWeths(etherToken, etherTokenState)} - -
-
-
-
- ); - } - private _renderActionColumnTitle(isBidirectional: boolean) { - let iconClass = 'zmdi-long-arrow-right'; - let leftSymbol = 'WETH'; - let rightSymbol = 'ETH'; - if (isBidirectional) { - iconClass = 'zmdi-swap'; - leftSymbol = 'ETH'; - rightSymbol = 'WETH'; - } - return ( -
-
{leftSymbol}
-
- -
-
{rightSymbol}
-
- ); - } - private _renderOutdatedWeths(etherToken: Token, etherTokenState: TokenState) { - const rows = _.map( - configs.OUTDATED_WRAPPED_ETHERS, - (outdatedWETHByNetworkId: OutdatedWrappedEtherByNetworkId) => { - const outdatedWETHIfExists = outdatedWETHByNetworkId[this.props.networkId]; - if (_.isUndefined(outdatedWETHIfExists)) { - return null; // noop - } - const timestampMsRange = outdatedWETHIfExists.timestampMsRange; - let dateRange: string; - if (!_.isUndefined(timestampMsRange)) { - const startMoment = moment(timestampMsRange.startTimestampMs); - const endMoment = moment(timestampMsRange.endTimestampMs); - dateRange = `${startMoment.format(DATE_FORMAT)}-${endMoment.format(DATE_FORMAT)}`; - } else { - dateRange = '-'; - } - const outdatedEtherToken = { - ...etherToken, - address: outdatedWETHIfExists.address, - }; - const isStateLoaded = this.state.outdatedWETHAddressToIsStateLoaded[outdatedWETHIfExists.address]; - const outdatedEtherTokenState = this.state.outdatedWETHStateByAddress[outdatedWETHIfExists.address]; - const balanceInEthIfExists = isStateLoaded - ? ZeroEx.toUnitAmount(outdatedEtherTokenState.balance, constants.DECIMAL_PLACES_ETH).toFixed( - PRECISION, - ) - : undefined; - const onConversionSuccessful = this._onOutdatedConversionSuccessfulAsync.bind( - this, - outdatedWETHIfExists.address, - ); - const etherscanUrl = utils.getEtherScanLinkIfExists( - outdatedWETHIfExists.address, - this.props.networkId, - EtherscanLinkSuffixes.Address, - ); - const tokenLabel = this._renderToken(dateRange, outdatedEtherToken.address, OUTDATED_WETH_ICON_PATH); - return ( - - - {this._renderTokenLink(tokenLabel, etherscanUrl)} - - - {isStateLoaded ? ( - `${balanceInEthIfExists} WETH` - ) : ( - - )} - - - - - - ); - }, - ); - return rows; - } - private _renderTokenLink(tokenLabel: React.ReactNode, etherscanUrl: string) { - return ( - - {_.isUndefined(etherscanUrl) ? ( - tokenLabel - ) : ( - - {tokenLabel} - - )} - - ); - } - private _renderToken(name: string, address: string, imgPath: string) { - const tooltipId = `tooltip-${address}`; - return ( -
- -
- - {name} - - {address} -
-
- ); - } - private async _onOutdatedConversionSuccessfulAsync(outdatedWETHAddress: string) { - this.setState({ - outdatedWETHAddressToIsStateLoaded: { - ...this.state.outdatedWETHAddressToIsStateLoaded, - [outdatedWETHAddress]: false, - }, - }); - const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync( - this.props.userAddress, - outdatedWETHAddress, - ); - this.setState({ - outdatedWETHAddressToIsStateLoaded: { - ...this.state.outdatedWETHAddressToIsStateLoaded, - [outdatedWETHAddress]: true, - }, - outdatedWETHStateByAddress: { - ...this.state.outdatedWETHStateByAddress, - [outdatedWETHAddress]: { - balance, - allowance, - }, - }, - }); - } - private async _fetchOutdatedWETHStateAsync() { - const outdatedWETHAddresses = this._getOutdatedWETHAddresses(); - const outdatedWETHAddressToIsStateLoaded: OutdatedWETHAddressToIsStateLoaded = {}; - const outdatedWETHStateByAddress: OutdatedWETHStateByAddress = {}; - for (const address of outdatedWETHAddresses) { - const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync( - this.props.userAddress, - address, - ); - outdatedWETHStateByAddress[address] = { - balance, - allowance, - }; - outdatedWETHAddressToIsStateLoaded[address] = true; - } - this.setState({ - outdatedWETHStateByAddress, - outdatedWETHAddressToIsStateLoaded, - }); - } - private _getOutdatedWETHAddresses(): string[] { - const outdatedWETHAddresses = _.compact( - _.map(configs.OUTDATED_WRAPPED_ETHERS, outdatedWrappedEtherByNetwork => { - const outdatedWrappedEtherIfExists = outdatedWrappedEtherByNetwork[this.props.networkId]; - if (_.isUndefined(outdatedWrappedEtherIfExists)) { - return undefined; - } - const address = outdatedWrappedEtherIfExists.address; - return address; - }), - ); - return outdatedWETHAddresses; - } + constructor(props: EthWrappersProps) { + super(props); + const outdatedWETHAddresses = this._getOutdatedWETHAddresses(); + const outdatedWETHAddressToIsStateLoaded: OutdatedWETHAddressToIsStateLoaded = {}; + const outdatedWETHStateByAddress: OutdatedWETHStateByAddress = {}; + _.each(outdatedWETHAddresses, outdatedWETHAddress => { + outdatedWETHAddressToIsStateLoaded[outdatedWETHAddress] = false; + outdatedWETHStateByAddress[outdatedWETHAddress] = { + balance: new BigNumber(0), + allowance: new BigNumber(0), + }; + }); + this.state = { + outdatedWETHAddressToIsStateLoaded, + outdatedWETHStateByAddress, + }; + } + public componentDidMount() { + window.scrollTo(0, 0); + // tslint:disable-next-line:no-floating-promises + this._fetchOutdatedWETHStateAsync(); + } + public render() { + const tokens = _.values(this.props.tokenByAddress); + const etherToken = _.find(tokens, { symbol: 'WETH' }); + const etherTokenState = this.props.tokenStateByAddress[etherToken.address]; + const wethBalance = ZeroEx.toUnitAmount(etherTokenState.balance, constants.DECIMAL_PLACES_ETH); + const isBidirectional = true; + const etherscanUrl = utils.getEtherScanLinkIfExists( + etherToken.address, + this.props.networkId, + EtherscanLinkSuffixes.Address, + ); + const tokenLabel = this._renderToken('Wrapped Ether', etherToken.address, configs.ICON_URL_BY_SYMBOL.WETH); + return ( +
+
+

ETH Wrapper

+ +
+ +
+
Wrap ETH into an ERC20-compliant Ether token. 1 ETH = 1 WETH.
+
+ + + + ETH Token + Balance + + {this._renderActionColumnTitle(isBidirectional)} + + + + + + +
+ +
+ ETH +
+
+
+ + {this.props.userEtherBalance.toFixed(PRECISION)} ETH + + + + +
+ + + {this._renderTokenLink(tokenLabel, etherscanUrl)} + + {wethBalance.toFixed(PRECISION)} WETH + + + + +
+
+
+
+
+

Outdated WETH

+ +
+ The{' '} + + canonical WETH + {' '} + contract is updated when necessary. Unwrap outdated WETH in order to
 retrieve your ETH and move + it to the updated WETH token. +
+
+ + + + WETH Version + Balance + + {this._renderActionColumnTitle(!isBidirectional)} + + + + + {this._renderOutdatedWeths(etherToken, etherTokenState)} + +
+
+
+
+ ); + } + private _renderActionColumnTitle(isBidirectional: boolean) { + let iconClass = 'zmdi-long-arrow-right'; + let leftSymbol = 'WETH'; + let rightSymbol = 'ETH'; + if (isBidirectional) { + iconClass = 'zmdi-swap'; + leftSymbol = 'ETH'; + rightSymbol = 'WETH'; + } + return ( +
+
{leftSymbol}
+
+ +
+
{rightSymbol}
+
+ ); + } + private _renderOutdatedWeths(etherToken: Token, etherTokenState: TokenState) { + const rows = _.map( + configs.OUTDATED_WRAPPED_ETHERS, + (outdatedWETHByNetworkId: OutdatedWrappedEtherByNetworkId) => { + const outdatedWETHIfExists = outdatedWETHByNetworkId[this.props.networkId]; + if (_.isUndefined(outdatedWETHIfExists)) { + return null; // noop + } + const timestampMsRange = outdatedWETHIfExists.timestampMsRange; + let dateRange: string; + if (!_.isUndefined(timestampMsRange)) { + const startMoment = moment(timestampMsRange.startTimestampMs); + const endMoment = moment(timestampMsRange.endTimestampMs); + dateRange = `${startMoment.format(DATE_FORMAT)}-${endMoment.format(DATE_FORMAT)}`; + } else { + dateRange = '-'; + } + const outdatedEtherToken = { + ...etherToken, + address: outdatedWETHIfExists.address, + }; + const isStateLoaded = this.state.outdatedWETHAddressToIsStateLoaded[outdatedWETHIfExists.address]; + const outdatedEtherTokenState = this.state.outdatedWETHStateByAddress[outdatedWETHIfExists.address]; + const balanceInEthIfExists = isStateLoaded + ? ZeroEx.toUnitAmount(outdatedEtherTokenState.balance, constants.DECIMAL_PLACES_ETH).toFixed( + PRECISION, + ) + : undefined; + const onConversionSuccessful = this._onOutdatedConversionSuccessfulAsync.bind( + this, + outdatedWETHIfExists.address, + ); + const etherscanUrl = utils.getEtherScanLinkIfExists( + outdatedWETHIfExists.address, + this.props.networkId, + EtherscanLinkSuffixes.Address, + ); + const tokenLabel = this._renderToken(dateRange, outdatedEtherToken.address, OUTDATED_WETH_ICON_PATH); + return ( + + + {this._renderTokenLink(tokenLabel, etherscanUrl)} + + + {isStateLoaded ? ( + `${balanceInEthIfExists} WETH` + ) : ( + + )} + + + + + + ); + }, + ); + return rows; + } + private _renderTokenLink(tokenLabel: React.ReactNode, etherscanUrl: string) { + return ( + + {_.isUndefined(etherscanUrl) ? ( + tokenLabel + ) : ( + + {tokenLabel} + + )} + + ); + } + private _renderToken(name: string, address: string, imgPath: string) { + const tooltipId = `tooltip-${address}`; + return ( +
+ +
+ + {name} + + {address} +
+
+ ); + } + private async _onOutdatedConversionSuccessfulAsync(outdatedWETHAddress: string) { + this.setState({ + outdatedWETHAddressToIsStateLoaded: { + ...this.state.outdatedWETHAddressToIsStateLoaded, + [outdatedWETHAddress]: false, + }, + }); + const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync( + this.props.userAddress, + outdatedWETHAddress, + ); + this.setState({ + outdatedWETHAddressToIsStateLoaded: { + ...this.state.outdatedWETHAddressToIsStateLoaded, + [outdatedWETHAddress]: true, + }, + outdatedWETHStateByAddress: { + ...this.state.outdatedWETHStateByAddress, + [outdatedWETHAddress]: { + balance, + allowance, + }, + }, + }); + } + private async _fetchOutdatedWETHStateAsync() { + const outdatedWETHAddresses = this._getOutdatedWETHAddresses(); + const outdatedWETHAddressToIsStateLoaded: OutdatedWETHAddressToIsStateLoaded = {}; + const outdatedWETHStateByAddress: OutdatedWETHStateByAddress = {}; + for (const address of outdatedWETHAddresses) { + const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync( + this.props.userAddress, + address, + ); + outdatedWETHStateByAddress[address] = { + balance, + allowance, + }; + outdatedWETHAddressToIsStateLoaded[address] = true; + } + this.setState({ + outdatedWETHStateByAddress, + outdatedWETHAddressToIsStateLoaded, + }); + } + private _getOutdatedWETHAddresses(): string[] { + const outdatedWETHAddresses = _.compact( + _.map(configs.OUTDATED_WRAPPED_ETHERS, outdatedWrappedEtherByNetwork => { + const outdatedWrappedEtherIfExists = outdatedWrappedEtherByNetwork[this.props.networkId]; + if (_.isUndefined(outdatedWrappedEtherIfExists)) { + return undefined; + } + const address = outdatedWrappedEtherIfExists.address; + return address; + }), + ); + return outdatedWETHAddresses; + } } // tslint:disable:max-file-line-count diff --git a/packages/website/ts/components/fill_order.tsx b/packages/website/ts/components/fill_order.tsx index 249ee419e..1a150e9ee 100644 --- a/packages/website/ts/components/fill_order.tsx +++ b/packages/website/ts/components/fill_order.tsx @@ -26,665 +26,665 @@ import { errorReporter } from 'ts/utils/error_reporter'; import { utils } from 'ts/utils/utils'; interface FillOrderProps { - blockchain: Blockchain; - blockchainErr: BlockchainErrs; - orderFillAmount: BigNumber; - isOrderInUrl: boolean; - networkId: number; - userAddress: string; - tokenByAddress: TokenByAddress; - tokenStateByAddress: TokenStateByAddress; - initialOrder: Order; - dispatcher: Dispatcher; + blockchain: Blockchain; + blockchainErr: BlockchainErrs; + orderFillAmount: BigNumber; + isOrderInUrl: boolean; + networkId: number; + userAddress: string; + tokenByAddress: TokenByAddress; + tokenStateByAddress: TokenStateByAddress; + initialOrder: Order; + dispatcher: Dispatcher; } interface FillOrderState { - didOrderValidationRun: boolean; - areAllInvolvedTokensTracked: boolean; - globalErrMsg: string; - orderJSON: string; - orderJSONErrMsg: string; - parsedOrder: Order; - didFillOrderSucceed: boolean; - didCancelOrderSucceed: boolean; - unavailableTakerAmount: BigNumber; - isMakerTokenAddressInRegistry: boolean; - isTakerTokenAddressInRegistry: boolean; - isFillWarningDialogOpen: boolean; - isFilling: boolean; - isCancelling: boolean; - isConfirmingTokenTracking: boolean; - tokensToTrack: Token[]; + didOrderValidationRun: boolean; + areAllInvolvedTokensTracked: boolean; + globalErrMsg: string; + orderJSON: string; + orderJSONErrMsg: string; + parsedOrder: Order; + didFillOrderSucceed: boolean; + didCancelOrderSucceed: boolean; + unavailableTakerAmount: BigNumber; + isMakerTokenAddressInRegistry: boolean; + isTakerTokenAddressInRegistry: boolean; + isFillWarningDialogOpen: boolean; + isFilling: boolean; + isCancelling: boolean; + isConfirmingTokenTracking: boolean; + tokensToTrack: Token[]; } export class FillOrder extends React.Component { - private _validator: SchemaValidator; - constructor(props: FillOrderProps) { - super(props); - this.state = { - globalErrMsg: '', - didOrderValidationRun: false, - areAllInvolvedTokensTracked: false, - didFillOrderSucceed: false, - didCancelOrderSucceed: false, - orderJSON: _.isUndefined(this.props.initialOrder) ? '' : JSON.stringify(this.props.initialOrder), - orderJSONErrMsg: '', - parsedOrder: this.props.initialOrder, - unavailableTakerAmount: new BigNumber(0), - isMakerTokenAddressInRegistry: false, - isTakerTokenAddressInRegistry: false, - isFillWarningDialogOpen: false, - isFilling: false, - isCancelling: false, - isConfirmingTokenTracking: false, - tokensToTrack: [], - }; - this._validator = new SchemaValidator(); - } - public componentWillMount() { - if (!_.isEmpty(this.state.orderJSON)) { - // tslint:disable-next-line:no-floating-promises - this._validateFillOrderFireAndForgetAsync(this.state.orderJSON); - } - } - public componentDidMount() { - window.scrollTo(0, 0); - } - public render() { - return ( -
-

Fill an order

- -
- {!this.props.isOrderInUrl && ( -
-
Paste an order JSON snippet below to begin
-
Order JSON
- - {this._renderOrderJsonNotices()} -
- )} -
- {!_.isUndefined(this.state.parsedOrder) && - this.state.didOrderValidationRun && - this.state.areAllInvolvedTokensTracked && - this._renderVisualOrder()} -
- {this.props.isOrderInUrl && ( -
- - - - - - - {this._renderOrderJsonNotices()} -
- )} -
- - -
- ); - } - private _renderOrderJsonNotices() { - return ( -
- {!_.isUndefined(this.props.initialOrder) && - !this.state.didOrderValidationRun && ( -
- - - - Validating order... -
- )} - {!_.isEmpty(this.state.orderJSONErrMsg) && ( - - )} -
- ); - } - private _renderVisualOrder() { - const takerTokenAddress = this.state.parsedOrder.taker.token.address; - const takerToken = this.props.tokenByAddress[takerTokenAddress]; - const orderTakerAmount = new BigNumber(this.state.parsedOrder.taker.amount); - const orderMakerAmount = new BigNumber(this.state.parsedOrder.maker.amount); - const takerAssetToken = { - amount: orderTakerAmount.minus(this.state.unavailableTakerAmount), - symbol: takerToken.symbol, - }; - const fillToken = this.props.tokenByAddress[takerToken.address]; - const fillTokenState = this.props.tokenStateByAddress[takerToken.address]; - const makerTokenAddress = this.state.parsedOrder.maker.token.address; - const makerToken = this.props.tokenByAddress[makerTokenAddress]; - const makerAssetToken = { - amount: orderMakerAmount.times(takerAssetToken.amount).div(orderTakerAmount), - symbol: makerToken.symbol, - }; - const fillAssetToken = { - amount: this.props.orderFillAmount, - symbol: takerToken.symbol, - }; - const orderTaker = !_.isEmpty(this.state.parsedOrder.taker.address) - ? this.state.parsedOrder.taker.address - : this.props.userAddress; - const parsedOrderExpiration = new BigNumber(this.state.parsedOrder.expiration); - const exchangeRate = orderMakerAmount.div(orderTakerAmount); + private _validator: SchemaValidator; + constructor(props: FillOrderProps) { + super(props); + this.state = { + globalErrMsg: '', + didOrderValidationRun: false, + areAllInvolvedTokensTracked: false, + didFillOrderSucceed: false, + didCancelOrderSucceed: false, + orderJSON: _.isUndefined(this.props.initialOrder) ? '' : JSON.stringify(this.props.initialOrder), + orderJSONErrMsg: '', + parsedOrder: this.props.initialOrder, + unavailableTakerAmount: new BigNumber(0), + isMakerTokenAddressInRegistry: false, + isTakerTokenAddressInRegistry: false, + isFillWarningDialogOpen: false, + isFilling: false, + isCancelling: false, + isConfirmingTokenTracking: false, + tokensToTrack: [], + }; + this._validator = new SchemaValidator(); + } + public componentWillMount() { + if (!_.isEmpty(this.state.orderJSON)) { + // tslint:disable-next-line:no-floating-promises + this._validateFillOrderFireAndForgetAsync(this.state.orderJSON); + } + } + public componentDidMount() { + window.scrollTo(0, 0); + } + public render() { + return ( +
+

Fill an order

+ +
+ {!this.props.isOrderInUrl && ( +
+
Paste an order JSON snippet below to begin
+
Order JSON
+ + {this._renderOrderJsonNotices()} +
+ )} +
+ {!_.isUndefined(this.state.parsedOrder) && + this.state.didOrderValidationRun && + this.state.areAllInvolvedTokensTracked && + this._renderVisualOrder()} +
+ {this.props.isOrderInUrl && ( +
+ + + + + + + {this._renderOrderJsonNotices()} +
+ )} +
+ + +
+ ); + } + private _renderOrderJsonNotices() { + return ( +
+ {!_.isUndefined(this.props.initialOrder) && + !this.state.didOrderValidationRun && ( +
+ + + + Validating order... +
+ )} + {!_.isEmpty(this.state.orderJSONErrMsg) && ( + + )} +
+ ); + } + private _renderVisualOrder() { + const takerTokenAddress = this.state.parsedOrder.taker.token.address; + const takerToken = this.props.tokenByAddress[takerTokenAddress]; + const orderTakerAmount = new BigNumber(this.state.parsedOrder.taker.amount); + const orderMakerAmount = new BigNumber(this.state.parsedOrder.maker.amount); + const takerAssetToken = { + amount: orderTakerAmount.minus(this.state.unavailableTakerAmount), + symbol: takerToken.symbol, + }; + const fillToken = this.props.tokenByAddress[takerToken.address]; + const fillTokenState = this.props.tokenStateByAddress[takerToken.address]; + const makerTokenAddress = this.state.parsedOrder.maker.token.address; + const makerToken = this.props.tokenByAddress[makerTokenAddress]; + const makerAssetToken = { + amount: orderMakerAmount.times(takerAssetToken.amount).div(orderTakerAmount), + symbol: makerToken.symbol, + }; + const fillAssetToken = { + amount: this.props.orderFillAmount, + symbol: takerToken.symbol, + }; + const orderTaker = !_.isEmpty(this.state.parsedOrder.taker.address) + ? this.state.parsedOrder.taker.address + : this.props.userAddress; + const parsedOrderExpiration = new BigNumber(this.state.parsedOrder.expiration); + const exchangeRate = orderMakerAmount.div(orderTakerAmount); - let orderReceiveAmount = 0; - if (!_.isUndefined(this.props.orderFillAmount)) { - const orderReceiveAmountBigNumber = exchangeRate.mul(this.props.orderFillAmount); - orderReceiveAmount = this._formatCurrencyAmount(orderReceiveAmountBigNumber, makerToken.decimals); - } - const isUserMaker = - !_.isUndefined(this.state.parsedOrder) && this.state.parsedOrder.maker.address === this.props.userAddress; - const expiryDate = utils.convertToReadableDateTimeFromUnixTimestamp(parsedOrderExpiration); - return ( -
-
-
Order details
-
-
- Maker: -
-
- -
-
- -
-
-
-
-
- -
Expires: {expiryDate} UTC
-
-
- {!isUserMaker && ( -
- -
- = {accounting.formatNumber(orderReceiveAmount, 6)} {makerToken.symbol} -
-
- )} -
- {isUserMaker ? ( -
- - {this.state.didCancelOrderSucceed && ( - - )} -
- ) : ( -
- - {!_.isEmpty(this.state.globalErrMsg) && ( - - )} - {this.state.didFillOrderSucceed && ( - - )} -
- )} -
-
- ); - } - private _renderFillSuccessMsg() { - return ( -
- Order successfully filled. See the trade details in your{' '} - - trade history - -
- ); - } - private _renderCancelSuccessMsg() { - return
Order successfully cancelled.
; - } - private _onFillOrderClick() { - if (!this.state.isMakerTokenAddressInRegistry || !this.state.isTakerTokenAddressInRegistry) { - this.setState({ - isFillWarningDialogOpen: true, - }); - } else { - // tslint:disable-next-line:no-floating-promises - this._onFillOrderClickFireAndForgetAsync(); - } - } - private _onFillWarningClosed(didUserCancel: boolean) { - this.setState({ - isFillWarningDialogOpen: false, - }); - if (!didUserCancel) { - // tslint:disable-next-line:no-floating-promises - this._onFillOrderClickFireAndForgetAsync(); - } - } - private _onFillAmountChange(isValid: boolean, amount?: BigNumber) { - this.props.dispatcher.updateOrderFillAmount(amount); - } - private _onFillOrderJSONChanged(event: any) { - const orderJSON = event.target.value; - this.setState({ - didOrderValidationRun: _.isEmpty(orderJSON) && _.isEmpty(this.state.orderJSONErrMsg), - didFillOrderSucceed: false, - }); - // tslint:disable-next-line:no-floating-promises - this._validateFillOrderFireAndForgetAsync(orderJSON); - } - private async _checkForUntrackedTokensAndAskToAdd() { - if (!_.isEmpty(this.state.orderJSONErrMsg)) { - return; - } + let orderReceiveAmount = 0; + if (!_.isUndefined(this.props.orderFillAmount)) { + const orderReceiveAmountBigNumber = exchangeRate.mul(this.props.orderFillAmount); + orderReceiveAmount = this._formatCurrencyAmount(orderReceiveAmountBigNumber, makerToken.decimals); + } + const isUserMaker = + !_.isUndefined(this.state.parsedOrder) && this.state.parsedOrder.maker.address === this.props.userAddress; + const expiryDate = utils.convertToReadableDateTimeFromUnixTimestamp(parsedOrderExpiration); + return ( +
+
+
Order details
+
+
+ Maker: +
+
+ +
+
+ +
+
+
+
+
+ +
Expires: {expiryDate} UTC
+
+
+ {!isUserMaker && ( +
+ +
+ = {accounting.formatNumber(orderReceiveAmount, 6)} {makerToken.symbol} +
+
+ )} +
+ {isUserMaker ? ( +
+ + {this.state.didCancelOrderSucceed && ( + + )} +
+ ) : ( +
+ + {!_.isEmpty(this.state.globalErrMsg) && ( + + )} + {this.state.didFillOrderSucceed && ( + + )} +
+ )} +
+
+ ); + } + private _renderFillSuccessMsg() { + return ( +
+ Order successfully filled. See the trade details in your{' '} + + trade history + +
+ ); + } + private _renderCancelSuccessMsg() { + return
Order successfully cancelled.
; + } + private _onFillOrderClick() { + if (!this.state.isMakerTokenAddressInRegistry || !this.state.isTakerTokenAddressInRegistry) { + this.setState({ + isFillWarningDialogOpen: true, + }); + } else { + // tslint:disable-next-line:no-floating-promises + this._onFillOrderClickFireAndForgetAsync(); + } + } + private _onFillWarningClosed(didUserCancel: boolean) { + this.setState({ + isFillWarningDialogOpen: false, + }); + if (!didUserCancel) { + // tslint:disable-next-line:no-floating-promises + this._onFillOrderClickFireAndForgetAsync(); + } + } + private _onFillAmountChange(isValid: boolean, amount?: BigNumber) { + this.props.dispatcher.updateOrderFillAmount(amount); + } + private _onFillOrderJSONChanged(event: any) { + const orderJSON = event.target.value; + this.setState({ + didOrderValidationRun: _.isEmpty(orderJSON) && _.isEmpty(this.state.orderJSONErrMsg), + didFillOrderSucceed: false, + }); + // tslint:disable-next-line:no-floating-promises + this._validateFillOrderFireAndForgetAsync(orderJSON); + } + private async _checkForUntrackedTokensAndAskToAdd() { + if (!_.isEmpty(this.state.orderJSONErrMsg)) { + return; + } - const makerTokenIfExists = this.props.tokenByAddress[this.state.parsedOrder.maker.token.address]; - const takerTokenIfExists = this.props.tokenByAddress[this.state.parsedOrder.taker.token.address]; + const makerTokenIfExists = this.props.tokenByAddress[this.state.parsedOrder.maker.token.address]; + const takerTokenIfExists = this.props.tokenByAddress[this.state.parsedOrder.taker.token.address]; - const tokensToTrack = []; - const isUnseenMakerToken = _.isUndefined(makerTokenIfExists); - const isMakerTokenTracked = !_.isUndefined(makerTokenIfExists) && makerTokenIfExists.isTracked; - if (isUnseenMakerToken) { - tokensToTrack.push({ - ...this.state.parsedOrder.maker.token, - iconUrl: undefined, - isTracked: false, - isRegistered: false, - }); - } else if (!isMakerTokenTracked) { - tokensToTrack.push(makerTokenIfExists); - } - const isUnseenTakerToken = _.isUndefined(takerTokenIfExists); - const isTakerTokenTracked = !_.isUndefined(takerTokenIfExists) && takerTokenIfExists.isTracked; - if (isUnseenTakerToken) { - tokensToTrack.push({ - ...this.state.parsedOrder.taker.token, - iconUrl: undefined, - isTracked: false, - isRegistered: false, - }); - } else if (!isTakerTokenTracked) { - tokensToTrack.push(takerTokenIfExists); - } - if (!_.isEmpty(tokensToTrack)) { - this.setState({ - isConfirmingTokenTracking: true, - tokensToTrack, - }); - } else { - this.setState({ - areAllInvolvedTokensTracked: true, - }); - } - } - private async _validateFillOrderFireAndForgetAsync(orderJSON: string) { - let orderJSONErrMsg = ''; - let parsedOrder: Order; - try { - const order = JSON.parse(orderJSON); - const validationResult = this._validator.validate(order, orderSchema); - if (validationResult.errors.length > 0) { - orderJSONErrMsg = 'Submitted order JSON is not a valid order'; - utils.consoleLog(`Unexpected order JSON validation error: ${validationResult.errors.join(', ')}`); - return; - } - parsedOrder = order; + const tokensToTrack = []; + const isUnseenMakerToken = _.isUndefined(makerTokenIfExists); + const isMakerTokenTracked = !_.isUndefined(makerTokenIfExists) && makerTokenIfExists.isTracked; + if (isUnseenMakerToken) { + tokensToTrack.push({ + ...this.state.parsedOrder.maker.token, + iconUrl: undefined, + isTracked: false, + isRegistered: false, + }); + } else if (!isMakerTokenTracked) { + tokensToTrack.push(makerTokenIfExists); + } + const isUnseenTakerToken = _.isUndefined(takerTokenIfExists); + const isTakerTokenTracked = !_.isUndefined(takerTokenIfExists) && takerTokenIfExists.isTracked; + if (isUnseenTakerToken) { + tokensToTrack.push({ + ...this.state.parsedOrder.taker.token, + iconUrl: undefined, + isTracked: false, + isRegistered: false, + }); + } else if (!isTakerTokenTracked) { + tokensToTrack.push(takerTokenIfExists); + } + if (!_.isEmpty(tokensToTrack)) { + this.setState({ + isConfirmingTokenTracking: true, + tokensToTrack, + }); + } else { + this.setState({ + areAllInvolvedTokensTracked: true, + }); + } + } + private async _validateFillOrderFireAndForgetAsync(orderJSON: string) { + let orderJSONErrMsg = ''; + let parsedOrder: Order; + try { + const order = JSON.parse(orderJSON); + const validationResult = this._validator.validate(order, orderSchema); + if (validationResult.errors.length > 0) { + orderJSONErrMsg = 'Submitted order JSON is not a valid order'; + utils.consoleLog(`Unexpected order JSON validation error: ${validationResult.errors.join(', ')}`); + return; + } + parsedOrder = order; - const exchangeContractAddr = this.props.blockchain.getExchangeContractAddressIfExists(); - const makerAmount = new BigNumber(parsedOrder.maker.amount); - const takerAmount = new BigNumber(parsedOrder.taker.amount); - const expiration = new BigNumber(parsedOrder.expiration); - const salt = new BigNumber(parsedOrder.salt); - const parsedMakerFee = new BigNumber(parsedOrder.maker.feeAmount); - const parsedTakerFee = new BigNumber(parsedOrder.taker.feeAmount); + const exchangeContractAddr = this.props.blockchain.getExchangeContractAddressIfExists(); + const makerAmount = new BigNumber(parsedOrder.maker.amount); + const takerAmount = new BigNumber(parsedOrder.taker.amount); + const expiration = new BigNumber(parsedOrder.expiration); + const salt = new BigNumber(parsedOrder.salt); + const parsedMakerFee = new BigNumber(parsedOrder.maker.feeAmount); + const parsedTakerFee = new BigNumber(parsedOrder.taker.feeAmount); - const zeroExOrder: ZeroExOrder = { - exchangeContractAddress: parsedOrder.exchangeContract, - expirationUnixTimestampSec: expiration, - feeRecipient: parsedOrder.feeRecipient, - maker: parsedOrder.maker.address, - makerFee: parsedMakerFee, - makerTokenAddress: parsedOrder.maker.token.address, - makerTokenAmount: makerAmount, - salt, - taker: _.isEmpty(parsedOrder.taker.address) ? constants.NULL_ADDRESS : parsedOrder.taker.address, - takerFee: parsedTakerFee, - takerTokenAddress: parsedOrder.taker.token.address, - takerTokenAmount: takerAmount, - }; - const orderHash = ZeroEx.getOrderHashHex(zeroExOrder); + const zeroExOrder: ZeroExOrder = { + exchangeContractAddress: parsedOrder.exchangeContract, + expirationUnixTimestampSec: expiration, + feeRecipient: parsedOrder.feeRecipient, + maker: parsedOrder.maker.address, + makerFee: parsedMakerFee, + makerTokenAddress: parsedOrder.maker.token.address, + makerTokenAmount: makerAmount, + salt, + taker: _.isEmpty(parsedOrder.taker.address) ? constants.NULL_ADDRESS : parsedOrder.taker.address, + takerFee: parsedTakerFee, + takerTokenAddress: parsedOrder.taker.token.address, + takerTokenAmount: takerAmount, + }; + const orderHash = ZeroEx.getOrderHashHex(zeroExOrder); - const signature = parsedOrder.signature; - const isValidSignature = ZeroEx.isValidSignature(signature.hash, signature, parsedOrder.maker.address); - if (this.props.networkId !== parsedOrder.networkId) { - orderJSONErrMsg = `This order was made on another Ethereum network + const signature = parsedOrder.signature; + const isValidSignature = ZeroEx.isValidSignature(signature.hash, signature, parsedOrder.maker.address); + if (this.props.networkId !== parsedOrder.networkId) { + orderJSONErrMsg = `This order was made on another Ethereum network (id: ${parsedOrder.networkId}). Connect to this network to fill.`; - parsedOrder = undefined; - } else if (exchangeContractAddr !== parsedOrder.exchangeContract) { - orderJSONErrMsg = 'This order was made using a deprecated 0x Exchange contract.'; - parsedOrder = undefined; - } else if (orderHash !== signature.hash) { - orderJSONErrMsg = 'Order hash does not match supplied plaintext values'; - parsedOrder = undefined; - } else if (!isValidSignature) { - orderJSONErrMsg = 'Order signature is invalid'; - parsedOrder = undefined; - } else { - // Update user supplied order cache so that if they navigate away from fill view - // e.g to set a token allowance, when they come back, the fill order persists - this.props.dispatcher.updateUserSuppliedOrderCache(parsedOrder); - } - } catch (err) { - utils.consoleLog(`Validate order err: ${err}`); - if (!_.isEmpty(orderJSON)) { - orderJSONErrMsg = 'Submitted order JSON is not valid JSON'; - } - this.setState({ - didOrderValidationRun: true, - orderJSON, - orderJSONErrMsg, - parsedOrder, - }); - return; - } + parsedOrder = undefined; + } else if (exchangeContractAddr !== parsedOrder.exchangeContract) { + orderJSONErrMsg = 'This order was made using a deprecated 0x Exchange contract.'; + parsedOrder = undefined; + } else if (orderHash !== signature.hash) { + orderJSONErrMsg = 'Order hash does not match supplied plaintext values'; + parsedOrder = undefined; + } else if (!isValidSignature) { + orderJSONErrMsg = 'Order signature is invalid'; + parsedOrder = undefined; + } else { + // Update user supplied order cache so that if they navigate away from fill view + // e.g to set a token allowance, when they come back, the fill order persists + this.props.dispatcher.updateUserSuppliedOrderCache(parsedOrder); + } + } catch (err) { + utils.consoleLog(`Validate order err: ${err}`); + if (!_.isEmpty(orderJSON)) { + orderJSONErrMsg = 'Submitted order JSON is not valid JSON'; + } + this.setState({ + didOrderValidationRun: true, + orderJSON, + orderJSONErrMsg, + parsedOrder, + }); + return; + } - let unavailableTakerAmount = new BigNumber(0); - if (!_.isEmpty(orderJSONErrMsg)) { - // Clear cache entry if user updates orderJSON to invalid entry - this.props.dispatcher.updateUserSuppliedOrderCache(undefined); - } else { - const orderHash = parsedOrder.signature.hash; - unavailableTakerAmount = await this.props.blockchain.getUnavailableTakerAmountAsync(orderHash); - const isMakerTokenAddressInRegistry = await this.props.blockchain.isAddressInTokenRegistryAsync( - parsedOrder.maker.token.address, - ); - const isTakerTokenAddressInRegistry = await this.props.blockchain.isAddressInTokenRegistryAsync( - parsedOrder.taker.token.address, - ); - this.setState({ - isMakerTokenAddressInRegistry, - isTakerTokenAddressInRegistry, - }); - } + let unavailableTakerAmount = new BigNumber(0); + if (!_.isEmpty(orderJSONErrMsg)) { + // Clear cache entry if user updates orderJSON to invalid entry + this.props.dispatcher.updateUserSuppliedOrderCache(undefined); + } else { + const orderHash = parsedOrder.signature.hash; + unavailableTakerAmount = await this.props.blockchain.getUnavailableTakerAmountAsync(orderHash); + const isMakerTokenAddressInRegistry = await this.props.blockchain.isAddressInTokenRegistryAsync( + parsedOrder.maker.token.address, + ); + const isTakerTokenAddressInRegistry = await this.props.blockchain.isAddressInTokenRegistryAsync( + parsedOrder.taker.token.address, + ); + this.setState({ + isMakerTokenAddressInRegistry, + isTakerTokenAddressInRegistry, + }); + } - this.setState({ - didOrderValidationRun: true, - orderJSON, - orderJSONErrMsg, - parsedOrder, - unavailableTakerAmount, - }); + this.setState({ + didOrderValidationRun: true, + orderJSON, + orderJSONErrMsg, + parsedOrder, + unavailableTakerAmount, + }); - await this._checkForUntrackedTokensAndAskToAdd(); - } - private async _onFillOrderClickFireAndForgetAsync(): Promise { - if (this.props.blockchainErr !== BlockchainErrs.NoError || _.isEmpty(this.props.userAddress)) { - this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); - return; - } + await this._checkForUntrackedTokensAndAskToAdd(); + } + private async _onFillOrderClickFireAndForgetAsync(): Promise { + if (this.props.blockchainErr !== BlockchainErrs.NoError || _.isEmpty(this.props.userAddress)) { + this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); + return; + } - this.setState({ - isFilling: true, - didFillOrderSucceed: false, - }); + this.setState({ + isFilling: true, + didFillOrderSucceed: false, + }); - const parsedOrder = this.state.parsedOrder; - const takerFillAmount = this.props.orderFillAmount; + const parsedOrder = this.state.parsedOrder; + const takerFillAmount = this.props.orderFillAmount; - if (_.isUndefined(this.props.userAddress)) { - this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); - this.setState({ - isFilling: false, - }); - return; - } - let globalErrMsg = ''; + if (_.isUndefined(this.props.userAddress)) { + this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); + this.setState({ + isFilling: false, + }); + return; + } + let globalErrMsg = ''; - if (_.isUndefined(takerFillAmount)) { - globalErrMsg = 'You must specify a fill amount'; - } + if (_.isUndefined(takerFillAmount)) { + globalErrMsg = 'You must specify a fill amount'; + } - const signedOrder = this.props.blockchain.portalOrderToSignedOrder( - parsedOrder.maker.address, - parsedOrder.taker.address, - parsedOrder.maker.token.address, - parsedOrder.taker.token.address, - new BigNumber(parsedOrder.maker.amount), - new BigNumber(parsedOrder.taker.amount), - new BigNumber(parsedOrder.maker.feeAmount), - new BigNumber(parsedOrder.taker.feeAmount), - new BigNumber(this.state.parsedOrder.expiration), - parsedOrder.feeRecipient, - parsedOrder.signature, - new BigNumber(parsedOrder.salt), - ); - if (_.isEmpty(globalErrMsg)) { - try { - await this.props.blockchain.validateFillOrderThrowIfInvalidAsync( - signedOrder, - takerFillAmount, - this.props.userAddress, - ); - } catch (err) { - globalErrMsg = utils.zeroExErrToHumanReadableErrMsg(err.message, parsedOrder.taker.address); - } - } - if (!_.isEmpty(globalErrMsg)) { - this.setState({ - isFilling: false, - globalErrMsg, - }); - return; - } - try { - const orderFilledAmount: BigNumber = await this.props.blockchain.fillOrderAsync( - signedOrder, - this.props.orderFillAmount, - ); - // After fill completes, let's update the token balances - const makerToken = this.props.tokenByAddress[parsedOrder.maker.token.address]; - const takerToken = this.props.tokenByAddress[parsedOrder.taker.token.address]; - const tokens = [makerToken, takerToken]; - await this.props.blockchain.updateTokenBalancesAndAllowancesAsync(tokens); - this.setState({ - isFilling: false, - didFillOrderSucceed: true, - globalErrMsg: '', - unavailableTakerAmount: this.state.unavailableTakerAmount.plus(orderFilledAmount), - }); - return; - } catch (err) { - this.setState({ - isFilling: false, - }); - const errMsg = `${err}`; - if (_.includes(errMsg, 'User denied transaction signature')) { - return; - } - globalErrMsg = 'Failed to fill order, please refresh and try again'; - utils.consoleLog(`${err}`); - this.setState({ - globalErrMsg, - }); - await errorReporter.reportAsync(err); - return; - } - } - private async _onCancelOrderClickFireAndForgetAsync(): Promise { - if (this.props.blockchainErr !== BlockchainErrs.NoError || _.isEmpty(this.props.userAddress)) { - this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); - return; - } + const signedOrder = this.props.blockchain.portalOrderToSignedOrder( + parsedOrder.maker.address, + parsedOrder.taker.address, + parsedOrder.maker.token.address, + parsedOrder.taker.token.address, + new BigNumber(parsedOrder.maker.amount), + new BigNumber(parsedOrder.taker.amount), + new BigNumber(parsedOrder.maker.feeAmount), + new BigNumber(parsedOrder.taker.feeAmount), + new BigNumber(this.state.parsedOrder.expiration), + parsedOrder.feeRecipient, + parsedOrder.signature, + new BigNumber(parsedOrder.salt), + ); + if (_.isEmpty(globalErrMsg)) { + try { + await this.props.blockchain.validateFillOrderThrowIfInvalidAsync( + signedOrder, + takerFillAmount, + this.props.userAddress, + ); + } catch (err) { + globalErrMsg = utils.zeroExErrToHumanReadableErrMsg(err.message, parsedOrder.taker.address); + } + } + if (!_.isEmpty(globalErrMsg)) { + this.setState({ + isFilling: false, + globalErrMsg, + }); + return; + } + try { + const orderFilledAmount: BigNumber = await this.props.blockchain.fillOrderAsync( + signedOrder, + this.props.orderFillAmount, + ); + // After fill completes, let's update the token balances + const makerToken = this.props.tokenByAddress[parsedOrder.maker.token.address]; + const takerToken = this.props.tokenByAddress[parsedOrder.taker.token.address]; + const tokens = [makerToken, takerToken]; + await this.props.blockchain.updateTokenBalancesAndAllowancesAsync(tokens); + this.setState({ + isFilling: false, + didFillOrderSucceed: true, + globalErrMsg: '', + unavailableTakerAmount: this.state.unavailableTakerAmount.plus(orderFilledAmount), + }); + return; + } catch (err) { + this.setState({ + isFilling: false, + }); + const errMsg = `${err}`; + if (_.includes(errMsg, 'User denied transaction signature')) { + return; + } + globalErrMsg = 'Failed to fill order, please refresh and try again'; + utils.consoleLog(`${err}`); + this.setState({ + globalErrMsg, + }); + await errorReporter.reportAsync(err); + return; + } + } + private async _onCancelOrderClickFireAndForgetAsync(): Promise { + if (this.props.blockchainErr !== BlockchainErrs.NoError || _.isEmpty(this.props.userAddress)) { + this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); + return; + } - this.setState({ - isCancelling: true, - didCancelOrderSucceed: false, - }); + this.setState({ + isCancelling: true, + didCancelOrderSucceed: false, + }); - const parsedOrder = this.state.parsedOrder; - const orderHash = parsedOrder.signature.hash; - const takerAddress = this.props.userAddress; + const parsedOrder = this.state.parsedOrder; + const orderHash = parsedOrder.signature.hash; + const takerAddress = this.props.userAddress; - if (_.isUndefined(takerAddress)) { - this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); - this.setState({ - isFilling: false, - }); - return; - } - let globalErrMsg = ''; + if (_.isUndefined(takerAddress)) { + this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); + this.setState({ + isFilling: false, + }); + return; + } + let globalErrMsg = ''; - const takerTokenAmount = new BigNumber(parsedOrder.taker.amount); + const takerTokenAmount = new BigNumber(parsedOrder.taker.amount); - const signedOrder = this.props.blockchain.portalOrderToSignedOrder( - parsedOrder.maker.address, - parsedOrder.taker.address, - parsedOrder.maker.token.address, - parsedOrder.taker.token.address, - new BigNumber(parsedOrder.maker.amount), - takerTokenAmount, - new BigNumber(parsedOrder.maker.feeAmount), - new BigNumber(parsedOrder.taker.feeAmount), - new BigNumber(this.state.parsedOrder.expiration), - parsedOrder.feeRecipient, - parsedOrder.signature, - new BigNumber(parsedOrder.salt), - ); - const unavailableTakerAmount = await this.props.blockchain.getUnavailableTakerAmountAsync(orderHash); - const availableTakerTokenAmount = takerTokenAmount.minus(unavailableTakerAmount); - try { - await this.props.blockchain.validateCancelOrderThrowIfInvalidAsync(signedOrder, availableTakerTokenAmount); - } catch (err) { - globalErrMsg = utils.zeroExErrToHumanReadableErrMsg(err.message, parsedOrder.taker.address); - } - if (!_.isEmpty(globalErrMsg)) { - this.setState({ - isCancelling: false, - globalErrMsg, - }); - return; - } - try { - await this.props.blockchain.cancelOrderAsync(signedOrder, availableTakerTokenAmount); - this.setState({ - isCancelling: false, - didCancelOrderSucceed: true, - globalErrMsg: '', - unavailableTakerAmount: takerTokenAmount, - }); - return; - } catch (err) { - this.setState({ - isCancelling: false, - }); - const errMsg = `${err}`; - if (_.includes(errMsg, 'User denied transaction signature')) { - return; - } - globalErrMsg = 'Failed to cancel order, please refresh and try again'; - utils.consoleLog(`${err}`); - this.setState({ - globalErrMsg, - }); - await errorReporter.reportAsync(err); - return; - } - } - private _formatCurrencyAmount(amount: BigNumber, decimals: number): number { - const unitAmount = ZeroEx.toUnitAmount(amount, decimals); - const roundedUnitAmount = Math.round(unitAmount.toNumber() * 100000) / 100000; - return roundedUnitAmount; - } - private _onToggleTrackConfirmDialog(didConfirmTokenTracking: boolean) { - if (!didConfirmTokenTracking) { - this.setState({ - orderJSON: '', - orderJSONErrMsg: '', - parsedOrder: undefined, - }); - } else { - this.setState({ - areAllInvolvedTokensTracked: true, - }); - } - this.setState({ - isConfirmingTokenTracking: !this.state.isConfirmingTokenTracking, - tokensToTrack: [], - }); - } + const signedOrder = this.props.blockchain.portalOrderToSignedOrder( + parsedOrder.maker.address, + parsedOrder.taker.address, + parsedOrder.maker.token.address, + parsedOrder.taker.token.address, + new BigNumber(parsedOrder.maker.amount), + takerTokenAmount, + new BigNumber(parsedOrder.maker.feeAmount), + new BigNumber(parsedOrder.taker.feeAmount), + new BigNumber(this.state.parsedOrder.expiration), + parsedOrder.feeRecipient, + parsedOrder.signature, + new BigNumber(parsedOrder.salt), + ); + const unavailableTakerAmount = await this.props.blockchain.getUnavailableTakerAmountAsync(orderHash); + const availableTakerTokenAmount = takerTokenAmount.minus(unavailableTakerAmount); + try { + await this.props.blockchain.validateCancelOrderThrowIfInvalidAsync(signedOrder, availableTakerTokenAmount); + } catch (err) { + globalErrMsg = utils.zeroExErrToHumanReadableErrMsg(err.message, parsedOrder.taker.address); + } + if (!_.isEmpty(globalErrMsg)) { + this.setState({ + isCancelling: false, + globalErrMsg, + }); + return; + } + try { + await this.props.blockchain.cancelOrderAsync(signedOrder, availableTakerTokenAmount); + this.setState({ + isCancelling: false, + didCancelOrderSucceed: true, + globalErrMsg: '', + unavailableTakerAmount: takerTokenAmount, + }); + return; + } catch (err) { + this.setState({ + isCancelling: false, + }); + const errMsg = `${err}`; + if (_.includes(errMsg, 'User denied transaction signature')) { + return; + } + globalErrMsg = 'Failed to cancel order, please refresh and try again'; + utils.consoleLog(`${err}`); + this.setState({ + globalErrMsg, + }); + await errorReporter.reportAsync(err); + return; + } + } + private _formatCurrencyAmount(amount: BigNumber, decimals: number): number { + const unitAmount = ZeroEx.toUnitAmount(amount, decimals); + const roundedUnitAmount = Math.round(unitAmount.toNumber() * 100000) / 100000; + return roundedUnitAmount; + } + private _onToggleTrackConfirmDialog(didConfirmTokenTracking: boolean) { + if (!didConfirmTokenTracking) { + this.setState({ + orderJSON: '', + orderJSONErrMsg: '', + parsedOrder: undefined, + }); + } else { + this.setState({ + areAllInvolvedTokensTracked: true, + }); + } + this.setState({ + isConfirmingTokenTracking: !this.state.isConfirmingTokenTracking, + tokensToTrack: [], + }); + } } // tslint:disable:max-file-line-count diff --git a/packages/website/ts/components/fill_order_json.tsx b/packages/website/ts/components/fill_order_json.tsx index 3446b8a39..f8e43481a 100644 --- a/packages/website/ts/components/fill_order_json.tsx +++ b/packages/website/ts/components/fill_order_json.tsx @@ -10,71 +10,71 @@ import { constants } from 'ts/utils/constants'; import { utils } from 'ts/utils/utils'; interface FillOrderJSONProps { - blockchain: Blockchain; - tokenByAddress: TokenByAddress; - networkId: number; - orderJSON: string; - onFillOrderJSONChanged: (event: any) => void; + blockchain: Blockchain; + tokenByAddress: TokenByAddress; + networkId: number; + orderJSON: string; + onFillOrderJSONChanged: (event: any) => void; } interface FillOrderJSONState {} export class FillOrderJSON extends React.Component { - public render() { - const tokenAddresses = _.keys(this.props.tokenByAddress); - const exchangeContract = this.props.blockchain.getExchangeContractAddressIfExists(); - const hintSideToAssetToken = { - [Side.Deposit]: { - amount: new BigNumber(35), - address: tokenAddresses[0], - }, - [Side.Receive]: { - amount: new BigNumber(89), - address: tokenAddresses[1], - }, - }; - const hintOrderExpiryTimestamp = utils.initialOrderExpiryUnixTimestampSec(); - const hintSignatureData = { - hash: '0xf965a9978a0381ab58f5a2408ad967c...', - r: '0xf01103f759e2289a28593eaf22e5820032...', - s: '937862111edcba395f8a9e0cc1b2c5e12320...', - v: 27, - }; - const hintSalt = ZeroEx.generatePseudoRandomSalt(); - const feeRecipient = constants.NULL_ADDRESS; - const hintOrder = utils.generateOrder( - this.props.networkId, - exchangeContract, - hintSideToAssetToken, - hintOrderExpiryTimestamp, - '', - '', - constants.MAKER_FEE, - constants.TAKER_FEE, - feeRecipient, - hintSignatureData, - this.props.tokenByAddress, - hintSalt, - ); - const hintOrderJSON = `${JSON.stringify(hintOrder, null, '\t').substring(0, 500)}...`; - return ( -
- - - -
- ); - } + public render() { + const tokenAddresses = _.keys(this.props.tokenByAddress); + const exchangeContract = this.props.blockchain.getExchangeContractAddressIfExists(); + const hintSideToAssetToken = { + [Side.Deposit]: { + amount: new BigNumber(35), + address: tokenAddresses[0], + }, + [Side.Receive]: { + amount: new BigNumber(89), + address: tokenAddresses[1], + }, + }; + const hintOrderExpiryTimestamp = utils.initialOrderExpiryUnixTimestampSec(); + const hintSignatureData = { + hash: '0xf965a9978a0381ab58f5a2408ad967c...', + r: '0xf01103f759e2289a28593eaf22e5820032...', + s: '937862111edcba395f8a9e0cc1b2c5e12320...', + v: 27, + }; + const hintSalt = ZeroEx.generatePseudoRandomSalt(); + const feeRecipient = constants.NULL_ADDRESS; + const hintOrder = utils.generateOrder( + this.props.networkId, + exchangeContract, + hintSideToAssetToken, + hintOrderExpiryTimestamp, + '', + '', + constants.MAKER_FEE, + constants.TAKER_FEE, + feeRecipient, + hintSignatureData, + this.props.tokenByAddress, + hintSalt, + ); + const hintOrderJSON = `${JSON.stringify(hintOrder, null, '\t').substring(0, 500)}...`; + return ( +
+ + + +
+ ); + } } diff --git a/packages/website/ts/components/fill_warning_dialog.tsx b/packages/website/ts/components/fill_warning_dialog.tsx index 40b04723d..165d21b34 100644 --- a/packages/website/ts/components/fill_warning_dialog.tsx +++ b/packages/website/ts/components/fill_warning_dialog.tsx @@ -4,42 +4,42 @@ import * as React from 'react'; import { colors } from 'ts/utils/colors'; interface FillWarningDialogProps { - isOpen: boolean; - onToggleDialog: (didUserCancel: boolean) => void; + isOpen: boolean; + onToggleDialog: (didUserCancel: boolean) => void; } export function FillWarningDialog(props: FillWarningDialogProps) { - const didCancel = true; - return ( - , - , - ]} - open={props.isOpen} - onRequestClose={props.onToggleDialog.bind(this)} - autoScrollBodyContent={true} - modal={true} - > -
-
- At least one of the tokens in this order was not found in the token registry smart contract and may - be counterfeit. It is your responsibility to verify the token addresses on Etherscan ( - - See this how-to guide - ) before filling an order. This action may result in the loss of funds. -
-
-
- ); + const didCancel = true; + return ( + , + , + ]} + open={props.isOpen} + onRequestClose={props.onToggleDialog.bind(this)} + autoScrollBodyContent={true} + modal={true} + > +
+
+ At least one of the tokens in this order was not found in the token registry smart contract and may + be counterfeit. It is your responsibility to verify the token addresses on Etherscan ( + + See this how-to guide + ) before filling an order. This action may result in the loss of funds. +
+
+
+ ); } diff --git a/packages/website/ts/components/flash_messages/token_send_completed.tsx b/packages/website/ts/components/flash_messages/token_send_completed.tsx index 07fca1ddc..18f371624 100644 --- a/packages/website/ts/components/flash_messages/token_send_completed.tsx +++ b/packages/website/ts/components/flash_messages/token_send_completed.tsx @@ -7,28 +7,28 @@ import { colors } from 'ts/utils/colors'; import { utils } from 'ts/utils/utils'; interface TokenSendCompletedProps { - etherScanLinkIfExists?: string; - token: Token; - toAddress: string; - amountInBaseUnits: BigNumber; + etherScanLinkIfExists?: string; + token: Token; + toAddress: string; + amountInBaseUnits: BigNumber; } interface TokenSendCompletedState {} export class TokenSendCompleted extends React.Component { - public render() { - const etherScanLink = !_.isUndefined(this.props.etherScanLinkIfExists) && ( - - Verify on Etherscan - - ); - const amountInUnits = ZeroEx.toUnitAmount(this.props.amountInBaseUnits, this.props.token.decimals); - const truncatedAddress = utils.getAddressBeginAndEnd(this.props.toAddress); - return ( -
- {`Sent ${amountInUnits} ${this.props.token.symbol} to ${truncatedAddress}: `} - {etherScanLink} -
- ); - } + public render() { + const etherScanLink = !_.isUndefined(this.props.etherScanLinkIfExists) && ( + + Verify on Etherscan + + ); + const amountInUnits = ZeroEx.toUnitAmount(this.props.amountInBaseUnits, this.props.token.decimals); + const truncatedAddress = utils.getAddressBeginAndEnd(this.props.toAddress); + return ( +
+ {`Sent ${amountInUnits} ${this.props.token.symbol} to ${truncatedAddress}: `} + {etherScanLink} +
+ ); + } } diff --git a/packages/website/ts/components/flash_messages/transaction_submitted.tsx b/packages/website/ts/components/flash_messages/transaction_submitted.tsx index 14464b923..862e382dd 100644 --- a/packages/website/ts/components/flash_messages/transaction_submitted.tsx +++ b/packages/website/ts/components/flash_messages/transaction_submitted.tsx @@ -3,24 +3,24 @@ import * as React from 'react'; import { colors } from 'ts/utils/colors'; interface TransactionSubmittedProps { - etherScanLinkIfExists?: string; + etherScanLinkIfExists?: string; } interface TransactionSubmittedState {} export class TransactionSubmitted extends React.Component { - public render() { - if (_.isUndefined(this.props.etherScanLinkIfExists)) { - return
Transaction submitted to the network
; - } else { - return ( -
- Transaction submitted to the network:{' '} - - Verify on Etherscan - -
- ); - } - } + public render() { + if (_.isUndefined(this.props.etherScanLinkIfExists)) { + return
Transaction submitted to the network
; + } else { + return ( +
+ Transaction submitted to the network:{' '} + + Verify on Etherscan + +
+ ); + } + } } diff --git a/packages/website/ts/components/footer.tsx b/packages/website/ts/components/footer.tsx index 3a342591c..a0f1a0c96 100644 --- a/packages/website/ts/components/footer.tsx +++ b/packages/website/ts/components/footer.tsx @@ -6,106 +6,106 @@ import { colors } from 'ts/utils/colors'; import { constants } from 'ts/utils/constants'; interface MenuItemsBySection { - [sectionName: string]: FooterMenuItem[]; + [sectionName: string]: FooterMenuItem[]; } interface FooterMenuItem { - title: string; - path?: string; - isExternal?: boolean; + title: string; + path?: string; + isExternal?: boolean; } enum Sections { - Documentation = 'Documentation', - Community = 'Community', - Organization = 'Organization', + Documentation = 'Documentation', + Community = 'Community', + Organization = 'Organization', } const ICON_DIMENSION = 16; const menuItemsBySection: MenuItemsBySection = { - Documentation: [ - { - title: '0x.js', - path: WebsitePaths.ZeroExJs, - }, - { - title: '0x Smart Contracts', - path: WebsitePaths.SmartContracts, - }, - { - title: '0x Connect', - path: WebsitePaths.Connect, - }, - { - title: 'Whitepaper', - path: WebsitePaths.Whitepaper, - isExternal: true, - }, - { - title: 'Wiki', - path: WebsitePaths.Wiki, - }, - { - title: 'FAQ', - path: WebsitePaths.FAQ, - }, - ], - Community: [ - { - title: 'Rocket.chat', - isExternal: true, - path: constants.URL_ZEROEX_CHAT, - }, - { - title: 'Blog', - isExternal: true, - path: constants.URL_BLOG, - }, - { - title: 'Twitter', - isExternal: true, - path: constants.URL_TWITTER, - }, - { - title: 'Reddit', - isExternal: true, - path: constants.URL_REDDIT, - }, - { - title: 'Forum', - isExternal: true, - path: constants.URL_DISCOURSE_FORUM, - }, - ], - Organization: [ - { - title: 'About', - isExternal: false, - path: WebsitePaths.About, - }, - { - title: 'Careers', - isExternal: true, - path: constants.URL_ANGELLIST, - }, - { - title: 'Contact', - isExternal: true, - path: 'mailto:team@0xproject.com', - }, - ], + Documentation: [ + { + title: '0x.js', + path: WebsitePaths.ZeroExJs, + }, + { + title: '0x Smart Contracts', + path: WebsitePaths.SmartContracts, + }, + { + title: '0x Connect', + path: WebsitePaths.Connect, + }, + { + title: 'Whitepaper', + path: WebsitePaths.Whitepaper, + isExternal: true, + }, + { + title: 'Wiki', + path: WebsitePaths.Wiki, + }, + { + title: 'FAQ', + path: WebsitePaths.FAQ, + }, + ], + Community: [ + { + title: 'Rocket.chat', + isExternal: true, + path: constants.URL_ZEROEX_CHAT, + }, + { + title: 'Blog', + isExternal: true, + path: constants.URL_BLOG, + }, + { + title: 'Twitter', + isExternal: true, + path: constants.URL_TWITTER, + }, + { + title: 'Reddit', + isExternal: true, + path: constants.URL_REDDIT, + }, + { + title: 'Forum', + isExternal: true, + path: constants.URL_DISCOURSE_FORUM, + }, + ], + Organization: [ + { + title: 'About', + isExternal: false, + path: WebsitePaths.About, + }, + { + title: 'Careers', + isExternal: true, + path: constants.URL_ANGELLIST, + }, + { + title: 'Contact', + isExternal: true, + path: 'mailto:team@0xproject.com', + }, + ], }; const linkStyle = { - color: colors.white, - cursor: 'pointer', + color: colors.white, + cursor: 'pointer', }; const titleToIcon: { [title: string]: string } = { - 'Rocket.chat': 'rocketchat.png', - Blog: 'medium.png', - Twitter: 'twitter.png', - Reddit: 'reddit.png', - Forum: 'discourse.png', + 'Rocket.chat': 'rocketchat.png', + Blog: 'medium.png', + Twitter: 'twitter.png', + Reddit: 'reddit.png', + Forum: 'discourse.png', }; export interface FooterProps {} @@ -113,100 +113,100 @@ export interface FooterProps {} interface FooterState {} export class Footer extends React.Component { - public render() { - return ( -
-
-
-
-
- -
-
- © ZeroEx, Intl. -
-
-
-
-
-
- {this._renderHeader(Sections.Documentation)} - {_.map(menuItemsBySection[Sections.Documentation], this._renderMenuItem.bind(this))} -
-
-
-
- {this._renderHeader(Sections.Community)} - {_.map(menuItemsBySection[Sections.Community], this._renderMenuItem.bind(this))} -
-
-
-
- {this._renderHeader(Sections.Organization)} - {_.map(menuItemsBySection[Sections.Organization], this._renderMenuItem.bind(this))} -
-
-
-
-
- ); - } - private _renderIcon(fileName: string) { - return ( -
- -
- ); - } - private _renderMenuItem(item: FooterMenuItem) { - const iconIfExists = titleToIcon[item.title]; - return ( -
- {item.isExternal ? ( - - {!_.isUndefined(iconIfExists) ? ( -
-
-
{this._renderIcon(iconIfExists)}
-
{item.title}
-
-
- ) : ( - item.title - )} -
- ) : ( - -
- {!_.isUndefined(iconIfExists) && ( -
{this._renderIcon(iconIfExists)}
- )} - {item.title} -
- - )} -
- ); - } - private _renderHeader(title: string) { - const headerStyle = { - textTransform: 'uppercase', - color: colors.grey400, - letterSpacing: 2, - fontFamily: 'Roboto Mono', - fontSize: 13, - }; - return ( -
- {title} -
- ); - } + public render() { + return ( +
+
+
+
+
+ +
+
+ © ZeroEx, Intl. +
+
+
+
+
+
+ {this._renderHeader(Sections.Documentation)} + {_.map(menuItemsBySection[Sections.Documentation], this._renderMenuItem.bind(this))} +
+
+
+
+ {this._renderHeader(Sections.Community)} + {_.map(menuItemsBySection[Sections.Community], this._renderMenuItem.bind(this))} +
+
+
+
+ {this._renderHeader(Sections.Organization)} + {_.map(menuItemsBySection[Sections.Organization], this._renderMenuItem.bind(this))} +
+
+
+
+
+ ); + } + private _renderIcon(fileName: string) { + return ( +
+ +
+ ); + } + private _renderMenuItem(item: FooterMenuItem) { + const iconIfExists = titleToIcon[item.title]; + return ( +
+ {item.isExternal ? ( + + {!_.isUndefined(iconIfExists) ? ( +
+
+
{this._renderIcon(iconIfExists)}
+
{item.title}
+
+
+ ) : ( + item.title + )} +
+ ) : ( + +
+ {!_.isUndefined(iconIfExists) && ( +
{this._renderIcon(iconIfExists)}
+ )} + {item.title} +
+ + )} +
+ ); + } + private _renderHeader(title: string) { + const headerStyle = { + textTransform: 'uppercase', + color: colors.grey400, + letterSpacing: 2, + fontFamily: 'Roboto Mono', + fontSize: 13, + }; + return ( +
+ {title} +
+ ); + } } diff --git a/packages/website/ts/components/generate_order/asset_picker.tsx b/packages/website/ts/components/generate_order/asset_picker.tsx index 5eed2fabf..df7d87cfd 100644 --- a/packages/website/ts/components/generate_order/asset_picker.tsx +++ b/packages/website/ts/components/generate_order/asset_picker.tsx @@ -13,264 +13,264 @@ import { DialogConfigs, Token, TokenByAddress, TokenState, TokenVisibility } fro const TOKEN_ICON_DIMENSION = 100; const TILE_DIMENSION = 146; enum AssetViews { - ASSET_PICKER = 'ASSET_PICKER', - NEW_TOKEN_FORM = 'NEW_TOKEN_FORM', - CONFIRM_TRACK_TOKEN = 'CONFIRM_TRACK_TOKEN', + ASSET_PICKER = 'ASSET_PICKER', + NEW_TOKEN_FORM = 'NEW_TOKEN_FORM', + CONFIRM_TRACK_TOKEN = 'CONFIRM_TRACK_TOKEN', } interface AssetPickerProps { - userAddress: string; - blockchain: Blockchain; - dispatcher: Dispatcher; - networkId: number; - isOpen: boolean; - currentTokenAddress: string; - onTokenChosen: (tokenAddress: string) => void; - tokenByAddress: TokenByAddress; - tokenVisibility?: TokenVisibility; + userAddress: string; + blockchain: Blockchain; + dispatcher: Dispatcher; + networkId: number; + isOpen: boolean; + currentTokenAddress: string; + onTokenChosen: (tokenAddress: string) => void; + tokenByAddress: TokenByAddress; + tokenVisibility?: TokenVisibility; } interface AssetPickerState { - assetView: AssetViews; - hoveredAddress: string | undefined; - chosenTrackTokenAddress: string; - isAddingTokenToTracked: boolean; + assetView: AssetViews; + hoveredAddress: string | undefined; + chosenTrackTokenAddress: string; + isAddingTokenToTracked: boolean; } export class AssetPicker extends React.Component { - public static defaultProps: Partial = { - tokenVisibility: TokenVisibility.ALL, - }; - private _dialogConfigsByAssetView: { [assetView: string]: DialogConfigs }; - constructor(props: AssetPickerProps) { - super(props); - this.state = { - assetView: AssetViews.ASSET_PICKER, - hoveredAddress: undefined, - chosenTrackTokenAddress: undefined, - isAddingTokenToTracked: false, - }; - this._dialogConfigsByAssetView = { - [AssetViews.ASSET_PICKER]: { - title: 'Select token', - isModal: false, - actions: [], - }, - [AssetViews.NEW_TOKEN_FORM]: { - title: 'Add an ERC20 token', - isModal: false, - actions: [], - }, - [AssetViews.CONFIRM_TRACK_TOKEN]: { - title: 'Tracking confirmation', - isModal: true, - actions: [ - , - , - ], - }, - }; - } - public render() { - const dialogConfigs: DialogConfigs = this._dialogConfigsByAssetView[this.state.assetView]; - return ( - - {this.state.assetView === AssetViews.ASSET_PICKER && this._renderAssetPicker()} - {this.state.assetView === AssetViews.NEW_TOKEN_FORM && ( - - )} - {this.state.assetView === AssetViews.CONFIRM_TRACK_TOKEN && this._renderConfirmTrackToken()} - - ); - } - private _renderConfirmTrackToken() { - const token = this.props.tokenByAddress[this.state.chosenTrackTokenAddress]; - return ( - - ); - } - private _renderAssetPicker() { - return ( -
- {this._renderGridTiles()} -
- ); - } - private _renderGridTiles() { - let isHovered; - let tileStyles; - const gridTiles = _.map(this.props.tokenByAddress, (token: Token, address: string) => { - if ( - (this.props.tokenVisibility === TokenVisibility.TRACKED && !token.isTracked) || - (this.props.tokenVisibility === TokenVisibility.UNTRACKED && token.isTracked) - ) { - return null; // Skip - } - isHovered = this.state.hoveredAddress === address; - tileStyles = { - cursor: 'pointer', - opacity: isHovered ? 0.6 : 1, - }; - return ( -
-
- -
-
{token.name}
-
- ); - }); - const otherTokenKey = 'otherToken'; - isHovered = this.state.hoveredAddress === otherTokenKey; - tileStyles = { - cursor: 'pointer', - opacity: isHovered ? 0.6 : 1, - }; - if (this.props.tokenVisibility !== TokenVisibility.TRACKED) { - gridTiles.push( -
-
- -
-
Other ERC20 Token
-
, - ); - } - return gridTiles; - } - private _onToggleHover(address: string, isHovered: boolean) { - const hoveredAddress = isHovered ? address : undefined; - this.setState({ - hoveredAddress, - }); - } - private _onCloseDialog() { - this.setState({ - assetView: AssetViews.ASSET_PICKER, - }); - this.props.onTokenChosen(this.props.currentTokenAddress); - } - private _onChooseToken(tokenAddress: string) { - const token = this.props.tokenByAddress[tokenAddress]; - if (token.isTracked) { - this.props.onTokenChosen(tokenAddress); - } else { - this.setState({ - assetView: AssetViews.CONFIRM_TRACK_TOKEN, - chosenTrackTokenAddress: tokenAddress, - }); - } - } - private _onCustomAssetChosen() { - this.setState({ - assetView: AssetViews.NEW_TOKEN_FORM, - }); - } - private _onNewTokenSubmitted(newToken: Token, newTokenState: TokenState) { - this.props.dispatcher.updateTokenStateByAddress({ - [newToken.address]: newTokenState, - }); - trackedTokenStorage.addTrackedTokenToUser(this.props.userAddress, this.props.networkId, newToken); - this.props.dispatcher.addTokenToTokenByAddress(newToken); - this.setState({ - assetView: AssetViews.ASSET_PICKER, - }); - this.props.onTokenChosen(newToken.address); - } - private async _onTrackConfirmationRespondedAsync(didUserAcceptTracking: boolean) { - if (!didUserAcceptTracking) { - this.setState({ - isAddingTokenToTracked: false, - assetView: AssetViews.ASSET_PICKER, - chosenTrackTokenAddress: undefined, - }); - this._onCloseDialog(); - return; - } - this.setState({ - isAddingTokenToTracked: true, - }); - const tokenAddress = this.state.chosenTrackTokenAddress; - const token = this.props.tokenByAddress[tokenAddress]; - const newTokenEntry = { - ...token, - }; + public static defaultProps: Partial = { + tokenVisibility: TokenVisibility.ALL, + }; + private _dialogConfigsByAssetView: { [assetView: string]: DialogConfigs }; + constructor(props: AssetPickerProps) { + super(props); + this.state = { + assetView: AssetViews.ASSET_PICKER, + hoveredAddress: undefined, + chosenTrackTokenAddress: undefined, + isAddingTokenToTracked: false, + }; + this._dialogConfigsByAssetView = { + [AssetViews.ASSET_PICKER]: { + title: 'Select token', + isModal: false, + actions: [], + }, + [AssetViews.NEW_TOKEN_FORM]: { + title: 'Add an ERC20 token', + isModal: false, + actions: [], + }, + [AssetViews.CONFIRM_TRACK_TOKEN]: { + title: 'Tracking confirmation', + isModal: true, + actions: [ + , + , + ], + }, + }; + } + public render() { + const dialogConfigs: DialogConfigs = this._dialogConfigsByAssetView[this.state.assetView]; + return ( + + {this.state.assetView === AssetViews.ASSET_PICKER && this._renderAssetPicker()} + {this.state.assetView === AssetViews.NEW_TOKEN_FORM && ( + + )} + {this.state.assetView === AssetViews.CONFIRM_TRACK_TOKEN && this._renderConfirmTrackToken()} + + ); + } + private _renderConfirmTrackToken() { + const token = this.props.tokenByAddress[this.state.chosenTrackTokenAddress]; + return ( + + ); + } + private _renderAssetPicker() { + return ( +
+ {this._renderGridTiles()} +
+ ); + } + private _renderGridTiles() { + let isHovered; + let tileStyles; + const gridTiles = _.map(this.props.tokenByAddress, (token: Token, address: string) => { + if ( + (this.props.tokenVisibility === TokenVisibility.TRACKED && !token.isTracked) || + (this.props.tokenVisibility === TokenVisibility.UNTRACKED && token.isTracked) + ) { + return null; // Skip + } + isHovered = this.state.hoveredAddress === address; + tileStyles = { + cursor: 'pointer', + opacity: isHovered ? 0.6 : 1, + }; + return ( +
+
+ +
+
{token.name}
+
+ ); + }); + const otherTokenKey = 'otherToken'; + isHovered = this.state.hoveredAddress === otherTokenKey; + tileStyles = { + cursor: 'pointer', + opacity: isHovered ? 0.6 : 1, + }; + if (this.props.tokenVisibility !== TokenVisibility.TRACKED) { + gridTiles.push( +
+
+ +
+
Other ERC20 Token
+
, + ); + } + return gridTiles; + } + private _onToggleHover(address: string, isHovered: boolean) { + const hoveredAddress = isHovered ? address : undefined; + this.setState({ + hoveredAddress, + }); + } + private _onCloseDialog() { + this.setState({ + assetView: AssetViews.ASSET_PICKER, + }); + this.props.onTokenChosen(this.props.currentTokenAddress); + } + private _onChooseToken(tokenAddress: string) { + const token = this.props.tokenByAddress[tokenAddress]; + if (token.isTracked) { + this.props.onTokenChosen(tokenAddress); + } else { + this.setState({ + assetView: AssetViews.CONFIRM_TRACK_TOKEN, + chosenTrackTokenAddress: tokenAddress, + }); + } + } + private _onCustomAssetChosen() { + this.setState({ + assetView: AssetViews.NEW_TOKEN_FORM, + }); + } + private _onNewTokenSubmitted(newToken: Token, newTokenState: TokenState) { + this.props.dispatcher.updateTokenStateByAddress({ + [newToken.address]: newTokenState, + }); + trackedTokenStorage.addTrackedTokenToUser(this.props.userAddress, this.props.networkId, newToken); + this.props.dispatcher.addTokenToTokenByAddress(newToken); + this.setState({ + assetView: AssetViews.ASSET_PICKER, + }); + this.props.onTokenChosen(newToken.address); + } + private async _onTrackConfirmationRespondedAsync(didUserAcceptTracking: boolean) { + if (!didUserAcceptTracking) { + this.setState({ + isAddingTokenToTracked: false, + assetView: AssetViews.ASSET_PICKER, + chosenTrackTokenAddress: undefined, + }); + this._onCloseDialog(); + return; + } + this.setState({ + isAddingTokenToTracked: true, + }); + const tokenAddress = this.state.chosenTrackTokenAddress; + const token = this.props.tokenByAddress[tokenAddress]; + const newTokenEntry = { + ...token, + }; - newTokenEntry.isTracked = true; - trackedTokenStorage.addTrackedTokenToUser(this.props.userAddress, this.props.networkId, newTokenEntry); + newTokenEntry.isTracked = true; + trackedTokenStorage.addTrackedTokenToUser(this.props.userAddress, this.props.networkId, newTokenEntry); - const [balance, allowance] = await this.props.blockchain.getCurrentUserTokenBalanceAndAllowanceAsync( - token.address, - ); - this.props.dispatcher.updateTokenStateByAddress({ - [token.address]: { - balance, - allowance, - }, - }); - this.props.dispatcher.updateTokenByAddress([newTokenEntry]); - this.setState({ - isAddingTokenToTracked: false, - assetView: AssetViews.ASSET_PICKER, - chosenTrackTokenAddress: undefined, - }); - this.props.onTokenChosen(tokenAddress); - } + const [balance, allowance] = await this.props.blockchain.getCurrentUserTokenBalanceAndAllowanceAsync( + token.address, + ); + this.props.dispatcher.updateTokenStateByAddress({ + [token.address]: { + balance, + allowance, + }, + }); + this.props.dispatcher.updateTokenByAddress([newTokenEntry]); + this.setState({ + isAddingTokenToTracked: false, + assetView: AssetViews.ASSET_PICKER, + chosenTrackTokenAddress: undefined, + }); + this.props.onTokenChosen(tokenAddress); + } } diff --git a/packages/website/ts/components/generate_order/generate_order_form.tsx b/packages/website/ts/components/generate_order/generate_order_form.tsx index b10b2d609..3ae0d48a7 100644 --- a/packages/website/ts/components/generate_order/generate_order_form.tsx +++ b/packages/website/ts/components/generate_order/generate_order_form.tsx @@ -19,335 +19,335 @@ import { Dispatcher } from 'ts/redux/dispatcher'; import { orderSchema } from 'ts/schemas/order_schema'; import { SchemaValidator } from 'ts/schemas/validator'; import { - AlertTypes, - BlockchainErrs, - HashData, - Side, - SideToAssetToken, - SignatureData, - Token, - TokenByAddress, - TokenStateByAddress, + AlertTypes, + BlockchainErrs, + HashData, + Side, + SideToAssetToken, + SignatureData, + Token, + TokenByAddress, + TokenStateByAddress, } from 'ts/types'; import { colors } from 'ts/utils/colors'; import { errorReporter } from 'ts/utils/error_reporter'; import { utils } from 'ts/utils/utils'; enum SigningState { - UNSIGNED, - SIGNING, - SIGNED, + UNSIGNED, + SIGNING, + SIGNED, } interface GenerateOrderFormProps { - blockchain: Blockchain; - blockchainErr: BlockchainErrs; - blockchainIsLoaded: boolean; - dispatcher: Dispatcher; - hashData: HashData; - orderExpiryTimestamp: BigNumber; - networkId: number; - userAddress: string; - orderSignatureData: SignatureData; - orderTakerAddress: string; - orderSalt: BigNumber; - sideToAssetToken: SideToAssetToken; - tokenByAddress: TokenByAddress; - tokenStateByAddress: TokenStateByAddress; + blockchain: Blockchain; + blockchainErr: BlockchainErrs; + blockchainIsLoaded: boolean; + dispatcher: Dispatcher; + hashData: HashData; + orderExpiryTimestamp: BigNumber; + networkId: number; + userAddress: string; + orderSignatureData: SignatureData; + orderTakerAddress: string; + orderSalt: BigNumber; + sideToAssetToken: SideToAssetToken; + tokenByAddress: TokenByAddress; + tokenStateByAddress: TokenStateByAddress; } interface GenerateOrderFormState { - globalErrMsg: string; - shouldShowIncompleteErrs: boolean; - signingState: SigningState; + globalErrMsg: string; + shouldShowIncompleteErrs: boolean; + signingState: SigningState; } export class GenerateOrderForm extends React.Component { - private _validator: SchemaValidator; - constructor(props: GenerateOrderFormProps) { - super(props); - this.state = { - globalErrMsg: '', - shouldShowIncompleteErrs: false, - signingState: SigningState.UNSIGNED, - }; - this._validator = new SchemaValidator(); - } - public componentDidMount() { - window.scrollTo(0, 0); - } - public render() { - const dispatcher = this.props.dispatcher; - const depositTokenAddress = this.props.sideToAssetToken[Side.Deposit].address; - const depositToken = this.props.tokenByAddress[depositTokenAddress]; - const depositTokenState = this.props.tokenStateByAddress[depositTokenAddress]; - const receiveTokenAddress = this.props.sideToAssetToken[Side.Receive].address; - const receiveToken = this.props.tokenByAddress[receiveTokenAddress]; - const receiveTokenState = this.props.tokenStateByAddress[receiveTokenAddress]; - const takerExplanation = - 'If a taker is specified, only they are
\ + private _validator: SchemaValidator; + constructor(props: GenerateOrderFormProps) { + super(props); + this.state = { + globalErrMsg: '', + shouldShowIncompleteErrs: false, + signingState: SigningState.UNSIGNED, + }; + this._validator = new SchemaValidator(); + } + public componentDidMount() { + window.scrollTo(0, 0); + } + public render() { + const dispatcher = this.props.dispatcher; + const depositTokenAddress = this.props.sideToAssetToken[Side.Deposit].address; + const depositToken = this.props.tokenByAddress[depositTokenAddress]; + const depositTokenState = this.props.tokenStateByAddress[depositTokenAddress]; + const receiveTokenAddress = this.props.sideToAssetToken[Side.Receive].address; + const receiveToken = this.props.tokenByAddress[receiveTokenAddress]; + const receiveTokenState = this.props.tokenStateByAddress[receiveTokenAddress]; + const takerExplanation = + 'If a taker is specified, only they are
\ allowed to fill this order. If no taker is
\ specified, anyone is able to fill it.'; - const exchangeContractIfExists = this.props.blockchain.getExchangeContractAddressIfExists(); - return ( -
-

Generate an order

- -
-
-
-
- - -
-
-
- -
-
-
- - -
-
-
-
-
-
Expiration
- -
-
-
- -
-
- -
-
-
-
- -
-
-
- -
- {this.state.globalErrMsg !== '' && ( - - )} -
-
- - - -
- ); - } - private _onTokenAmountChange(token: Token, side: Side, isValid: boolean, amount?: BigNumber) { - this.props.dispatcher.updateChosenAssetToken(side, { - address: token.address, - amount, - }); - } - private _onCloseOrderJSONDialog() { - // Upon closing the order JSON dialog, we update the orderSalt stored in the Redux store - // with a new value so that if a user signs the identical order again, the newly signed - // orderHash will not collide with the previously generated orderHash. - this.props.dispatcher.updateOrderSalt(ZeroEx.generatePseudoRandomSalt()); - this.setState({ - signingState: SigningState.UNSIGNED, - }); - } - private async _onSignClickedAsync(): Promise { - if (this.props.blockchainErr !== BlockchainErrs.NoError) { - this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); - return false; - } + const exchangeContractIfExists = this.props.blockchain.getExchangeContractAddressIfExists(); + return ( +
+

Generate an order

+ +
+
+
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+
+
+
Expiration
+ +
+
+
+ +
+
+ +
+
+
+
+ +
+
+
+ +
+ {this.state.globalErrMsg !== '' && ( + + )} +
+
+ + + +
+ ); + } + private _onTokenAmountChange(token: Token, side: Side, isValid: boolean, amount?: BigNumber) { + this.props.dispatcher.updateChosenAssetToken(side, { + address: token.address, + amount, + }); + } + private _onCloseOrderJSONDialog() { + // Upon closing the order JSON dialog, we update the orderSalt stored in the Redux store + // with a new value so that if a user signs the identical order again, the newly signed + // orderHash will not collide with the previously generated orderHash. + this.props.dispatcher.updateOrderSalt(ZeroEx.generatePseudoRandomSalt()); + this.setState({ + signingState: SigningState.UNSIGNED, + }); + } + private async _onSignClickedAsync(): Promise { + if (this.props.blockchainErr !== BlockchainErrs.NoError) { + this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); + return false; + } - // Check if all required inputs were supplied - const debitToken = this.props.sideToAssetToken[Side.Deposit]; - const debitBalance = this.props.tokenStateByAddress[debitToken.address].balance; - const debitAllowance = this.props.tokenStateByAddress[debitToken.address].allowance; - const receiveAmount = this.props.sideToAssetToken[Side.Receive].amount; - if ( - !_.isUndefined(debitToken.amount) && - !_.isUndefined(receiveAmount) && - debitToken.amount.gt(0) && - receiveAmount.gt(0) && - this.props.userAddress !== '' && - debitBalance.gte(debitToken.amount) && - debitAllowance.gte(debitToken.amount) - ) { - const didSignSuccessfully = await this._signTransactionAsync(); - if (didSignSuccessfully) { - this.setState({ - globalErrMsg: '', - shouldShowIncompleteErrs: false, - }); - } - return didSignSuccessfully; - } else { - let globalErrMsg = 'You must fix the above errors in order to generate a valid order'; - if (this.props.userAddress === '') { - globalErrMsg = 'You must enable wallet communication'; - this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); - } - this.setState({ - globalErrMsg, - shouldShowIncompleteErrs: true, - }); - return false; - } - } - private async _signTransactionAsync(): Promise { - this.setState({ - signingState: SigningState.SIGNING, - }); - const exchangeContractAddr = this.props.blockchain.getExchangeContractAddressIfExists(); - if (_.isUndefined(exchangeContractAddr)) { - this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); - this.setState({ - signingState: SigningState.UNSIGNED, - }); - return false; - } - const hashData = this.props.hashData; + // Check if all required inputs were supplied + const debitToken = this.props.sideToAssetToken[Side.Deposit]; + const debitBalance = this.props.tokenStateByAddress[debitToken.address].balance; + const debitAllowance = this.props.tokenStateByAddress[debitToken.address].allowance; + const receiveAmount = this.props.sideToAssetToken[Side.Receive].amount; + if ( + !_.isUndefined(debitToken.amount) && + !_.isUndefined(receiveAmount) && + debitToken.amount.gt(0) && + receiveAmount.gt(0) && + this.props.userAddress !== '' && + debitBalance.gte(debitToken.amount) && + debitAllowance.gte(debitToken.amount) + ) { + const didSignSuccessfully = await this._signTransactionAsync(); + if (didSignSuccessfully) { + this.setState({ + globalErrMsg: '', + shouldShowIncompleteErrs: false, + }); + } + return didSignSuccessfully; + } else { + let globalErrMsg = 'You must fix the above errors in order to generate a valid order'; + if (this.props.userAddress === '') { + globalErrMsg = 'You must enable wallet communication'; + this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); + } + this.setState({ + globalErrMsg, + shouldShowIncompleteErrs: true, + }); + return false; + } + } + private async _signTransactionAsync(): Promise { + this.setState({ + signingState: SigningState.SIGNING, + }); + const exchangeContractAddr = this.props.blockchain.getExchangeContractAddressIfExists(); + if (_.isUndefined(exchangeContractAddr)) { + this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); + this.setState({ + signingState: SigningState.UNSIGNED, + }); + return false; + } + const hashData = this.props.hashData; - const zeroExOrder: Order = { - exchangeContractAddress: exchangeContractAddr, - expirationUnixTimestampSec: hashData.orderExpiryTimestamp, - feeRecipient: hashData.feeRecipientAddress, - maker: hashData.orderMakerAddress, - makerFee: hashData.makerFee, - makerTokenAddress: hashData.depositTokenContractAddr, - makerTokenAmount: hashData.depositAmount, - salt: hashData.orderSalt, - taker: hashData.orderTakerAddress, - takerFee: hashData.takerFee, - takerTokenAddress: hashData.receiveTokenContractAddr, - takerTokenAmount: hashData.receiveAmount, - }; - const orderHash = ZeroEx.getOrderHashHex(zeroExOrder); + const zeroExOrder: Order = { + exchangeContractAddress: exchangeContractAddr, + expirationUnixTimestampSec: hashData.orderExpiryTimestamp, + feeRecipient: hashData.feeRecipientAddress, + maker: hashData.orderMakerAddress, + makerFee: hashData.makerFee, + makerTokenAddress: hashData.depositTokenContractAddr, + makerTokenAmount: hashData.depositAmount, + salt: hashData.orderSalt, + taker: hashData.orderTakerAddress, + takerFee: hashData.takerFee, + takerTokenAddress: hashData.receiveTokenContractAddr, + takerTokenAmount: hashData.receiveAmount, + }; + const orderHash = ZeroEx.getOrderHashHex(zeroExOrder); - let globalErrMsg = ''; - try { - const signatureData = await this.props.blockchain.signOrderHashAsync(orderHash); - const order = utils.generateOrder( - this.props.networkId, - exchangeContractAddr, - this.props.sideToAssetToken, - hashData.orderExpiryTimestamp, - this.props.orderTakerAddress, - this.props.userAddress, - hashData.makerFee, - hashData.takerFee, - hashData.feeRecipientAddress, - signatureData, - this.props.tokenByAddress, - hashData.orderSalt, - ); - const validationResult = this._validator.validate(order, orderSchema); - if (validationResult.errors.length > 0) { - globalErrMsg = 'Order signing failed. Please refresh and try again'; - utils.consoleLog(`Unexpected error occured: Order validation failed: + let globalErrMsg = ''; + try { + const signatureData = await this.props.blockchain.signOrderHashAsync(orderHash); + const order = utils.generateOrder( + this.props.networkId, + exchangeContractAddr, + this.props.sideToAssetToken, + hashData.orderExpiryTimestamp, + this.props.orderTakerAddress, + this.props.userAddress, + hashData.makerFee, + hashData.takerFee, + hashData.feeRecipientAddress, + signatureData, + this.props.tokenByAddress, + hashData.orderSalt, + ); + const validationResult = this._validator.validate(order, orderSchema); + if (validationResult.errors.length > 0) { + globalErrMsg = 'Order signing failed. Please refresh and try again'; + utils.consoleLog(`Unexpected error occured: Order validation failed: ${validationResult.errors}`); - } - } catch (err) { - const errMsg = `${err}`; - if (utils.didUserDenyWeb3Request(errMsg)) { - globalErrMsg = 'User denied sign request'; - } else { - globalErrMsg = 'An unexpected error occured. Please try refreshing the page'; - utils.consoleLog(`Unexpected error occured: ${err}`); - utils.consoleLog(err.stack); - await errorReporter.reportAsync(err); - } - } - this.setState({ - signingState: globalErrMsg === '' ? SigningState.SIGNED : SigningState.UNSIGNED, - globalErrMsg, - }); - return globalErrMsg === ''; - } - private _updateOrderAddress(address?: string): void { - if (!_.isUndefined(address)) { - this.props.dispatcher.updateOrderTakerAddress(address); - } - } + } + } catch (err) { + const errMsg = `${err}`; + if (utils.didUserDenyWeb3Request(errMsg)) { + globalErrMsg = 'User denied sign request'; + } else { + globalErrMsg = 'An unexpected error occured. Please try refreshing the page'; + utils.consoleLog(`Unexpected error occured: ${err}`); + utils.consoleLog(err.stack); + await errorReporter.reportAsync(err); + } + } + this.setState({ + signingState: globalErrMsg === '' ? SigningState.SIGNED : SigningState.UNSIGNED, + globalErrMsg, + }); + return globalErrMsg === ''; + } + private _updateOrderAddress(address?: string): void { + if (!_.isUndefined(address)) { + this.props.dispatcher.updateOrderTakerAddress(address); + } + } } diff --git a/packages/website/ts/components/generate_order/new_token_form.tsx b/packages/website/ts/components/generate_order/new_token_form.tsx index d61aac92a..63645be9a 100644 --- a/packages/website/ts/components/generate_order/new_token_form.tsx +++ b/packages/website/ts/components/generate_order/new_token_form.tsx @@ -11,227 +11,227 @@ import { AlertTypes, Token, TokenByAddress, TokenState } from 'ts/types'; import { colors } from 'ts/utils/colors'; interface NewTokenFormProps { - blockchain: Blockchain; - tokenByAddress: TokenByAddress; - onNewTokenSubmitted: (token: Token, tokenState: TokenState) => void; + blockchain: Blockchain; + tokenByAddress: TokenByAddress; + onNewTokenSubmitted: (token: Token, tokenState: TokenState) => void; } interface NewTokenFormState { - globalErrMsg: string; - name: string; - nameErrText: string; - symbol: string; - symbolErrText: string; - address: string; - shouldShowAddressIncompleteErr: boolean; - decimals: string; - decimalsErrText: string; + globalErrMsg: string; + name: string; + nameErrText: string; + symbol: string; + symbolErrText: string; + address: string; + shouldShowAddressIncompleteErr: boolean; + decimals: string; + decimalsErrText: string; } export class NewTokenForm extends React.Component { - constructor(props: NewTokenFormProps) { - super(props); - this.state = { - address: '', - globalErrMsg: '', - name: '', - nameErrText: '', - shouldShowAddressIncompleteErr: false, - symbol: '', - symbolErrText: '', - decimals: '18', - decimalsErrText: '', - }; - } - public render() { - return ( -
-
- } - value={this.state.name} - errorText={this.state.nameErrText} - onChange={this._onTokenNameChanged.bind(this)} - /> -
-
- } - value={this.state.symbol} - errorText={this.state.symbolErrText} - onChange={this._onTokenSymbolChanged.bind(this)} - /> -
-
- -
-
- } - value={this.state.decimals} - errorText={this.state.decimalsErrText} - onChange={this._onTokenDecimalsChanged.bind(this)} - /> -
-
- -
- {this.state.globalErrMsg !== '' && } -
- ); - } - private async _onAddNewTokenClickAsync() { - // Trigger validation of name and symbol - this._onTokenNameChanged(undefined, this.state.name); - this._onTokenSymbolChanged(undefined, this.state.symbol); - this._onTokenDecimalsChanged(undefined, this.state.decimals); + constructor(props: NewTokenFormProps) { + super(props); + this.state = { + address: '', + globalErrMsg: '', + name: '', + nameErrText: '', + shouldShowAddressIncompleteErr: false, + symbol: '', + symbolErrText: '', + decimals: '18', + decimalsErrText: '', + }; + } + public render() { + return ( +
+
+ } + value={this.state.name} + errorText={this.state.nameErrText} + onChange={this._onTokenNameChanged.bind(this)} + /> +
+
+ } + value={this.state.symbol} + errorText={this.state.symbolErrText} + onChange={this._onTokenSymbolChanged.bind(this)} + /> +
+
+ +
+
+ } + value={this.state.decimals} + errorText={this.state.decimalsErrText} + onChange={this._onTokenDecimalsChanged.bind(this)} + /> +
+
+ +
+ {this.state.globalErrMsg !== '' && } +
+ ); + } + private async _onAddNewTokenClickAsync() { + // Trigger validation of name and symbol + this._onTokenNameChanged(undefined, this.state.name); + this._onTokenSymbolChanged(undefined, this.state.symbol); + this._onTokenDecimalsChanged(undefined, this.state.decimals); - const isAddressIncomplete = this.state.address === ''; - let doesContractExist = false; - if (!isAddressIncomplete) { - doesContractExist = await this.props.blockchain.doesContractExistAtAddressAsync(this.state.address); - } + const isAddressIncomplete = this.state.address === ''; + let doesContractExist = false; + if (!isAddressIncomplete) { + doesContractExist = await this.props.blockchain.doesContractExistAtAddressAsync(this.state.address); + } - let hasBalanceAllowanceErr = false; - let balance = new BigNumber(0); - let allowance = new BigNumber(0); - if (doesContractExist) { - try { - [balance, allowance] = await this.props.blockchain.getCurrentUserTokenBalanceAndAllowanceAsync( - this.state.address, - ); - } catch (err) { - hasBalanceAllowanceErr = true; - } - } + let hasBalanceAllowanceErr = false; + let balance = new BigNumber(0); + let allowance = new BigNumber(0); + if (doesContractExist) { + try { + [balance, allowance] = await this.props.blockchain.getCurrentUserTokenBalanceAndAllowanceAsync( + this.state.address, + ); + } catch (err) { + hasBalanceAllowanceErr = true; + } + } - let globalErrMsg = ''; - if ( - this.state.nameErrText !== '' || - this.state.symbolErrText !== '' || - this.state.decimalsErrText !== '' || - isAddressIncomplete - ) { - globalErrMsg = 'Please fix the above issues'; - } else if (!doesContractExist) { - globalErrMsg = 'No contract found at supplied address'; - } else if (hasBalanceAllowanceErr) { - globalErrMsg = 'Unsuccessful call to `balanceOf` and/or `allowance` on supplied contract address'; - } else if (!isAddressIncomplete && !_.isUndefined(this.props.tokenByAddress[this.state.address])) { - globalErrMsg = 'A token already exists with this address'; - } + let globalErrMsg = ''; + if ( + this.state.nameErrText !== '' || + this.state.symbolErrText !== '' || + this.state.decimalsErrText !== '' || + isAddressIncomplete + ) { + globalErrMsg = 'Please fix the above issues'; + } else if (!doesContractExist) { + globalErrMsg = 'No contract found at supplied address'; + } else if (hasBalanceAllowanceErr) { + globalErrMsg = 'Unsuccessful call to `balanceOf` and/or `allowance` on supplied contract address'; + } else if (!isAddressIncomplete && !_.isUndefined(this.props.tokenByAddress[this.state.address])) { + globalErrMsg = 'A token already exists with this address'; + } - if (globalErrMsg !== '') { - this.setState({ - globalErrMsg, - shouldShowAddressIncompleteErr: isAddressIncomplete, - }); - return; - } + if (globalErrMsg !== '') { + this.setState({ + globalErrMsg, + shouldShowAddressIncompleteErr: isAddressIncomplete, + }); + return; + } - const newToken: Token = { - address: this.state.address, - decimals: _.parseInt(this.state.decimals), - iconUrl: undefined, - name: this.state.name, - symbol: this.state.symbol.toUpperCase(), - isTracked: true, - isRegistered: false, - }; - const newTokenState: TokenState = { - balance, - allowance, - }; - this.props.onNewTokenSubmitted(newToken, newTokenState); - } - private _onTokenNameChanged(e: any, name: string) { - let nameErrText = ''; - const maxLength = 30; - const tokens = _.values(this.props.tokenByAddress); - const tokenWithNameIfExists = _.find(tokens, { name }); - const tokenWithNameExists = !_.isUndefined(tokenWithNameIfExists); - if (name === '') { - nameErrText = 'Name is required'; - } else if (!this._isValidName(name)) { - nameErrText = 'Name should only contain letters, digits and spaces'; - } else if (name.length > maxLength) { - nameErrText = `Max length is ${maxLength}`; - } else if (tokenWithNameExists) { - nameErrText = 'Token with this name already exists'; - } + const newToken: Token = { + address: this.state.address, + decimals: _.parseInt(this.state.decimals), + iconUrl: undefined, + name: this.state.name, + symbol: this.state.symbol.toUpperCase(), + isTracked: true, + isRegistered: false, + }; + const newTokenState: TokenState = { + balance, + allowance, + }; + this.props.onNewTokenSubmitted(newToken, newTokenState); + } + private _onTokenNameChanged(e: any, name: string) { + let nameErrText = ''; + const maxLength = 30; + const tokens = _.values(this.props.tokenByAddress); + const tokenWithNameIfExists = _.find(tokens, { name }); + const tokenWithNameExists = !_.isUndefined(tokenWithNameIfExists); + if (name === '') { + nameErrText = 'Name is required'; + } else if (!this._isValidName(name)) { + nameErrText = 'Name should only contain letters, digits and spaces'; + } else if (name.length > maxLength) { + nameErrText = `Max length is ${maxLength}`; + } else if (tokenWithNameExists) { + nameErrText = 'Token with this name already exists'; + } - this.setState({ - name, - nameErrText, - }); - } - private _onTokenSymbolChanged(e: any, symbol: string) { - let symbolErrText = ''; - const maxLength = 5; - const tokens = _.values(this.props.tokenByAddress); - const tokenWithSymbolExists = !_.isUndefined(_.find(tokens, { symbol })); - if (symbol === '') { - symbolErrText = 'Symbol is required'; - } else if (!this._isAlphanumeric(symbol)) { - symbolErrText = 'Can only include alphanumeric characters'; - } else if (symbol.length > maxLength) { - symbolErrText = `Max length is ${maxLength}`; - } else if (tokenWithSymbolExists) { - symbolErrText = 'Token with symbol already exists'; - } + this.setState({ + name, + nameErrText, + }); + } + private _onTokenSymbolChanged(e: any, symbol: string) { + let symbolErrText = ''; + const maxLength = 5; + const tokens = _.values(this.props.tokenByAddress); + const tokenWithSymbolExists = !_.isUndefined(_.find(tokens, { symbol })); + if (symbol === '') { + symbolErrText = 'Symbol is required'; + } else if (!this._isAlphanumeric(symbol)) { + symbolErrText = 'Can only include alphanumeric characters'; + } else if (symbol.length > maxLength) { + symbolErrText = `Max length is ${maxLength}`; + } else if (tokenWithSymbolExists) { + symbolErrText = 'Token with symbol already exists'; + } - this.setState({ - symbol, - symbolErrText, - }); - } - private _onTokenDecimalsChanged(e: any, decimals: string) { - let decimalsErrText = ''; - const maxLength = 2; - if (decimals === '') { - decimalsErrText = 'Decimals is required'; - } else if (!this._isInteger(decimals)) { - decimalsErrText = 'Must be an integer'; - } else if (decimals.length > maxLength) { - decimalsErrText = `Max length is ${maxLength}`; - } + this.setState({ + symbol, + symbolErrText, + }); + } + private _onTokenDecimalsChanged(e: any, decimals: string) { + let decimalsErrText = ''; + const maxLength = 2; + if (decimals === '') { + decimalsErrText = 'Decimals is required'; + } else if (!this._isInteger(decimals)) { + decimalsErrText = 'Must be an integer'; + } else if (decimals.length > maxLength) { + decimalsErrText = `Max length is ${maxLength}`; + } - this.setState({ - decimals, - decimalsErrText, - }); - } - private _onTokenAddressChanged(address?: string) { - if (!_.isUndefined(address)) { - this.setState({ - address, - }); - } - } - private _isValidName(input: string) { - return /^[a-z0-9 ]+$/i.test(input); - } - private _isInteger(input: string) { - return /^[0-9]+$/i.test(input); - } - private _isAlphanumeric(input: string) { - return /^[a-zA-Z0-9]+$/i.test(input); - } + this.setState({ + decimals, + decimalsErrText, + }); + } + private _onTokenAddressChanged(address?: string) { + if (!_.isUndefined(address)) { + this.setState({ + address, + }); + } + } + private _isValidName(input: string) { + return /^[a-z0-9 ]+$/i.test(input); + } + private _isInteger(input: string) { + return /^[0-9]+$/i.test(input); + } + private _isAlphanumeric(input: string) { + return /^[a-zA-Z0-9]+$/i.test(input); + } } diff --git a/packages/website/ts/components/inputs/address_input.tsx b/packages/website/ts/components/inputs/address_input.tsx index 236bf9a00..dd4131140 100644 --- a/packages/website/ts/components/inputs/address_input.tsx +++ b/packages/website/ts/components/inputs/address_input.tsx @@ -6,66 +6,66 @@ import { RequiredLabel } from 'ts/components/ui/required_label'; import { colors } from 'ts/utils/colors'; interface AddressInputProps { - disabled?: boolean; - initialAddress: string; - isRequired?: boolean; - hintText?: string; - shouldHideLabel?: boolean; - label?: string; - shouldShowIncompleteErrs?: boolean; - updateAddress: (address?: string) => void; + disabled?: boolean; + initialAddress: string; + isRequired?: boolean; + hintText?: string; + shouldHideLabel?: boolean; + label?: string; + shouldShowIncompleteErrs?: boolean; + updateAddress: (address?: string) => void; } interface AddressInputState { - address: string; - errMsg: string; + address: string; + errMsg: string; } export class AddressInput extends React.Component { - constructor(props: AddressInputProps) { - super(props); - this.state = { - address: this.props.initialAddress, - errMsg: '', - }; - } - public componentWillReceiveProps(nextProps: AddressInputProps) { - if (nextProps.shouldShowIncompleteErrs && this.props.isRequired && this.state.address === '') { - this.setState({ - errMsg: 'Address is required', - }); - } - } - public render() { - const label = this.props.isRequired ? : this.props.label; - const labelDisplay = this.props.shouldHideLabel ? 'hidden' : 'block'; - const hintText = this.props.hintText ? this.props.hintText : ''; - return ( -
- -
- ); - } - private _onOrderTakerAddressUpdated(e: any) { - const address = e.target.value.toLowerCase(); - const isValidAddress = addressUtils.isAddress(address) || address === ''; - const errMsg = isValidAddress ? '' : 'Invalid ethereum address'; - this.setState({ - address, - errMsg, - }); - const addressIfValid = isValidAddress ? address : undefined; - this.props.updateAddress(addressIfValid); - } + constructor(props: AddressInputProps) { + super(props); + this.state = { + address: this.props.initialAddress, + errMsg: '', + }; + } + public componentWillReceiveProps(nextProps: AddressInputProps) { + if (nextProps.shouldShowIncompleteErrs && this.props.isRequired && this.state.address === '') { + this.setState({ + errMsg: 'Address is required', + }); + } + } + public render() { + const label = this.props.isRequired ? : this.props.label; + const labelDisplay = this.props.shouldHideLabel ? 'hidden' : 'block'; + const hintText = this.props.hintText ? this.props.hintText : ''; + return ( +
+ +
+ ); + } + private _onOrderTakerAddressUpdated(e: any) { + const address = e.target.value.toLowerCase(); + const isValidAddress = addressUtils.isAddress(address) || address === ''; + const errMsg = isValidAddress ? '' : 'Invalid ethereum address'; + this.setState({ + address, + errMsg, + }); + const addressIfValid = isValidAddress ? address : undefined; + this.props.updateAddress(addressIfValid); + } } diff --git a/packages/website/ts/components/inputs/allowance_toggle.tsx b/packages/website/ts/components/inputs/allowance_toggle.tsx index 245784824..da46db4f4 100644 --- a/packages/website/ts/components/inputs/allowance_toggle.tsx +++ b/packages/website/ts/components/inputs/allowance_toggle.tsx @@ -11,83 +11,83 @@ import { utils } from 'ts/utils/utils'; const DEFAULT_ALLOWANCE_AMOUNT_IN_BASE_UNITS = new BigNumber(2).pow(256).minus(1); interface AllowanceToggleProps { - blockchain: Blockchain; - dispatcher: Dispatcher; - onErrorOccurred: (errType: BalanceErrs) => void; - token: Token; - tokenState: TokenState; - userAddress: string; + blockchain: Blockchain; + dispatcher: Dispatcher; + onErrorOccurred: (errType: BalanceErrs) => void; + token: Token; + tokenState: TokenState; + userAddress: string; } interface AllowanceToggleState { - isSpinnerVisible: boolean; - prevAllowance: BigNumber; + isSpinnerVisible: boolean; + prevAllowance: BigNumber; } export class AllowanceToggle extends React.Component { - constructor(props: AllowanceToggleProps) { - super(props); - this.state = { - isSpinnerVisible: false, - prevAllowance: props.tokenState.allowance, - }; - } - public componentWillReceiveProps(nextProps: AllowanceToggleProps) { - if (!nextProps.tokenState.allowance.eq(this.state.prevAllowance)) { - this.setState({ - isSpinnerVisible: false, - prevAllowance: nextProps.tokenState.allowance, - }); - } - } - public render() { - return ( -
-
- -
- {this.state.isSpinnerVisible && ( -
- -
- )} -
- ); - } - private async _onToggleAllowanceAsync(): Promise { - if (this.props.userAddress === '') { - this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); - } + constructor(props: AllowanceToggleProps) { + super(props); + this.state = { + isSpinnerVisible: false, + prevAllowance: props.tokenState.allowance, + }; + } + public componentWillReceiveProps(nextProps: AllowanceToggleProps) { + if (!nextProps.tokenState.allowance.eq(this.state.prevAllowance)) { + this.setState({ + isSpinnerVisible: false, + prevAllowance: nextProps.tokenState.allowance, + }); + } + } + public render() { + return ( +
+
+ +
+ {this.state.isSpinnerVisible && ( +
+ +
+ )} +
+ ); + } + private async _onToggleAllowanceAsync(): Promise { + if (this.props.userAddress === '') { + this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); + } - this.setState({ - isSpinnerVisible: true, - }); + this.setState({ + isSpinnerVisible: true, + }); - let newAllowanceAmountInBaseUnits = new BigNumber(0); - if (!this._isAllowanceSet()) { - newAllowanceAmountInBaseUnits = DEFAULT_ALLOWANCE_AMOUNT_IN_BASE_UNITS; - } - try { - await this.props.blockchain.setProxyAllowanceAsync(this.props.token, newAllowanceAmountInBaseUnits); - } catch (err) { - this.setState({ - isSpinnerVisible: false, - }); - const errMsg = `${err}`; - if (_.includes(errMsg, 'User denied transaction')) { - return; - } - utils.consoleLog(`Unexpected error encountered: ${err}`); - utils.consoleLog(err.stack); - this.props.onErrorOccurred(BalanceErrs.allowanceSettingFailed); - await errorReporter.reportAsync(err); - } - } - private _isAllowanceSet() { - return !this.props.tokenState.allowance.eq(0); - } + let newAllowanceAmountInBaseUnits = new BigNumber(0); + if (!this._isAllowanceSet()) { + newAllowanceAmountInBaseUnits = DEFAULT_ALLOWANCE_AMOUNT_IN_BASE_UNITS; + } + try { + await this.props.blockchain.setProxyAllowanceAsync(this.props.token, newAllowanceAmountInBaseUnits); + } catch (err) { + this.setState({ + isSpinnerVisible: false, + }); + const errMsg = `${err}`; + if (_.includes(errMsg, 'User denied transaction')) { + return; + } + utils.consoleLog(`Unexpected error encountered: ${err}`); + utils.consoleLog(err.stack); + this.props.onErrorOccurred(BalanceErrs.allowanceSettingFailed); + await errorReporter.reportAsync(err); + } + } + private _isAllowanceSet() { + return !this.props.tokenState.allowance.eq(0); + } } diff --git a/packages/website/ts/components/inputs/balance_bounded_input.tsx b/packages/website/ts/components/inputs/balance_bounded_input.tsx index 5cc91994e..ddc434b51 100644 --- a/packages/website/ts/components/inputs/balance_bounded_input.tsx +++ b/packages/website/ts/components/inputs/balance_bounded_input.tsx @@ -9,143 +9,143 @@ import { colors } from 'ts/utils/colors'; import { utils } from 'ts/utils/utils'; interface BalanceBoundedInputProps { - label?: string; - balance: BigNumber; - amount?: BigNumber; - onChange: ValidatedBigNumberCallback; - shouldShowIncompleteErrs?: boolean; - shouldCheckBalance: boolean; - validate?: (amount: BigNumber) => InputErrMsg; - onVisitBalancesPageClick?: () => void; - shouldHideVisitBalancesLink?: boolean; + label?: string; + balance: BigNumber; + amount?: BigNumber; + onChange: ValidatedBigNumberCallback; + shouldShowIncompleteErrs?: boolean; + shouldCheckBalance: boolean; + validate?: (amount: BigNumber) => InputErrMsg; + onVisitBalancesPageClick?: () => void; + shouldHideVisitBalancesLink?: boolean; } interface BalanceBoundedInputState { - errMsg: InputErrMsg; - amountString: string; + errMsg: InputErrMsg; + amountString: string; } export class BalanceBoundedInput extends React.Component { - public static defaultProps: Partial = { - shouldShowIncompleteErrs: false, - shouldHideVisitBalancesLink: false, - }; - constructor(props: BalanceBoundedInputProps) { - super(props); - const amountString = this.props.amount ? this.props.amount.toString() : ''; - this.state = { - errMsg: this._validate(amountString, props.balance), - amountString, - }; - } - public componentWillReceiveProps(nextProps: BalanceBoundedInputProps) { - if (nextProps === this.props) { - return; - } - const isCurrentAmountNumeric = utils.isNumeric(this.state.amountString); - if (!_.isUndefined(nextProps.amount)) { - let shouldResetState = false; - if (!isCurrentAmountNumeric) { - shouldResetState = true; - } else { - const currentAmount = new BigNumber(this.state.amountString); - if (!currentAmount.eq(nextProps.amount) || !nextProps.balance.eq(this.props.balance)) { - shouldResetState = true; - } - } - if (shouldResetState) { - const amountString = nextProps.amount.toString(); - this.setState({ - errMsg: this._validate(amountString, nextProps.balance), - amountString, - }); - } - } else if (isCurrentAmountNumeric) { - const amountString = ''; - this.setState({ - errMsg: this._validate(amountString, nextProps.balance), - amountString, - }); - } - } - public render() { - let errorText = this.state.errMsg; - if (this.props.shouldShowIncompleteErrs && this.state.amountString === '') { - errorText = 'This field is required'; - } - let label: React.ReactNode | string = ''; - if (!_.isUndefined(this.props.label)) { - label = ; - } - return ( - amount} - onChange={this._onValueChange.bind(this)} - underlineStyle={{ width: 'calc(100% + 50px)' }} - /> - ); - } - private _onValueChange(e: any, amountString: string) { - const errMsg = this._validate(amountString, this.props.balance); - this.setState( - { - amountString, - errMsg, - }, - () => { - const isValid = _.isUndefined(errMsg); - if (utils.isNumeric(amountString)) { - this.props.onChange(isValid, new BigNumber(amountString)); - } else { - this.props.onChange(isValid); - } - }, - ); - } - private _validate(amountString: string, balance: BigNumber): InputErrMsg { - if (!utils.isNumeric(amountString)) { - return amountString !== '' ? 'Must be a number' : ''; - } - const amount = new BigNumber(amountString); - if (amount.eq(0)) { - return 'Cannot be zero'; - } - if (this.props.shouldCheckBalance && amount.gt(balance)) { - return Insufficient balance. {this._renderIncreaseBalanceLink()}; - } - const errMsg = _.isUndefined(this.props.validate) ? undefined : this.props.validate(amount); - return errMsg; - } - private _renderIncreaseBalanceLink() { - if (this.props.shouldHideVisitBalancesLink) { - return null; - } + public static defaultProps: Partial = { + shouldShowIncompleteErrs: false, + shouldHideVisitBalancesLink: false, + }; + constructor(props: BalanceBoundedInputProps) { + super(props); + const amountString = this.props.amount ? this.props.amount.toString() : ''; + this.state = { + errMsg: this._validate(amountString, props.balance), + amountString, + }; + } + public componentWillReceiveProps(nextProps: BalanceBoundedInputProps) { + if (nextProps === this.props) { + return; + } + const isCurrentAmountNumeric = utils.isNumeric(this.state.amountString); + if (!_.isUndefined(nextProps.amount)) { + let shouldResetState = false; + if (!isCurrentAmountNumeric) { + shouldResetState = true; + } else { + const currentAmount = new BigNumber(this.state.amountString); + if (!currentAmount.eq(nextProps.amount) || !nextProps.balance.eq(this.props.balance)) { + shouldResetState = true; + } + } + if (shouldResetState) { + const amountString = nextProps.amount.toString(); + this.setState({ + errMsg: this._validate(amountString, nextProps.balance), + amountString, + }); + } + } else if (isCurrentAmountNumeric) { + const amountString = ''; + this.setState({ + errMsg: this._validate(amountString, nextProps.balance), + amountString, + }); + } + } + public render() { + let errorText = this.state.errMsg; + if (this.props.shouldShowIncompleteErrs && this.state.amountString === '') { + errorText = 'This field is required'; + } + let label: React.ReactNode | string = ''; + if (!_.isUndefined(this.props.label)) { + label = ; + } + return ( + amount} + onChange={this._onValueChange.bind(this)} + underlineStyle={{ width: 'calc(100% + 50px)' }} + /> + ); + } + private _onValueChange(e: any, amountString: string) { + const errMsg = this._validate(amountString, this.props.balance); + this.setState( + { + amountString, + errMsg, + }, + () => { + const isValid = _.isUndefined(errMsg); + if (utils.isNumeric(amountString)) { + this.props.onChange(isValid, new BigNumber(amountString)); + } else { + this.props.onChange(isValid); + } + }, + ); + } + private _validate(amountString: string, balance: BigNumber): InputErrMsg { + if (!utils.isNumeric(amountString)) { + return amountString !== '' ? 'Must be a number' : ''; + } + const amount = new BigNumber(amountString); + if (amount.eq(0)) { + return 'Cannot be zero'; + } + if (this.props.shouldCheckBalance && amount.gt(balance)) { + return Insufficient balance. {this._renderIncreaseBalanceLink()}; + } + const errMsg = _.isUndefined(this.props.validate) ? undefined : this.props.validate(amount); + return errMsg; + } + private _renderIncreaseBalanceLink() { + if (this.props.shouldHideVisitBalancesLink) { + return null; + } - const increaseBalanceText = 'Increase balance'; - const linkStyle = { - cursor: 'pointer', - color: colors.darkestGrey, - textDecoration: 'underline', - display: 'inline', - }; - if (_.isUndefined(this.props.onVisitBalancesPageClick)) { - return ( - - {increaseBalanceText} - - ); - } else { - return ( -
- {increaseBalanceText} -
- ); - } - } + const increaseBalanceText = 'Increase balance'; + const linkStyle = { + cursor: 'pointer', + color: colors.darkestGrey, + textDecoration: 'underline', + display: 'inline', + }; + if (_.isUndefined(this.props.onVisitBalancesPageClick)) { + return ( + + {increaseBalanceText} + + ); + } else { + return ( +
+ {increaseBalanceText} +
+ ); + } + } } diff --git a/packages/website/ts/components/inputs/eth_amount_input.tsx b/packages/website/ts/components/inputs/eth_amount_input.tsx index 7f9747094..a66f92c8c 100644 --- a/packages/website/ts/components/inputs/eth_amount_input.tsx +++ b/packages/website/ts/components/inputs/eth_amount_input.tsx @@ -7,43 +7,43 @@ import { ValidatedBigNumberCallback } from 'ts/types'; import { constants } from 'ts/utils/constants'; interface EthAmountInputProps { - label?: string; - balance: BigNumber; - amount?: BigNumber; - onChange: ValidatedBigNumberCallback; - shouldShowIncompleteErrs: boolean; - onVisitBalancesPageClick?: () => void; - shouldCheckBalance: boolean; - shouldHideVisitBalancesLink?: boolean; + label?: string; + balance: BigNumber; + amount?: BigNumber; + onChange: ValidatedBigNumberCallback; + shouldShowIncompleteErrs: boolean; + onVisitBalancesPageClick?: () => void; + shouldCheckBalance: boolean; + shouldHideVisitBalancesLink?: boolean; } interface EthAmountInputState {} export class EthAmountInput extends React.Component { - public render() { - const amount = this.props.amount - ? ZeroEx.toUnitAmount(this.props.amount, constants.DECIMAL_PLACES_ETH) - : undefined; - return ( -
- -
ETH
-
- ); - } - private _onChange(isValid: boolean, amount?: BigNumber) { - const baseUnitAmountIfExists = _.isUndefined(amount) - ? undefined - : ZeroEx.toBaseUnitAmount(amount, constants.DECIMAL_PLACES_ETH); - this.props.onChange(isValid, baseUnitAmountIfExists); - } + public render() { + const amount = this.props.amount + ? ZeroEx.toUnitAmount(this.props.amount, constants.DECIMAL_PLACES_ETH) + : undefined; + return ( +
+ +
ETH
+
+ ); + } + private _onChange(isValid: boolean, amount?: BigNumber) { + const baseUnitAmountIfExists = _.isUndefined(amount) + ? undefined + : ZeroEx.toBaseUnitAmount(amount, constants.DECIMAL_PLACES_ETH); + this.props.onChange(isValid, baseUnitAmountIfExists); + } } diff --git a/packages/website/ts/components/inputs/expiration_input.tsx b/packages/website/ts/components/inputs/expiration_input.tsx index cb4ed7bd0..e473648d2 100644 --- a/packages/website/ts/components/inputs/expiration_input.tsx +++ b/packages/website/ts/components/inputs/expiration_input.tsx @@ -7,94 +7,94 @@ import * as React from 'react'; import { utils } from 'ts/utils/utils'; interface ExpirationInputProps { - orderExpiryTimestamp: BigNumber; - updateOrderExpiry: (unixTimestampSec: BigNumber) => void; + orderExpiryTimestamp: BigNumber; + updateOrderExpiry: (unixTimestampSec: BigNumber) => void; } interface ExpirationInputState { - dateMoment: moment.Moment; - timeMoment: moment.Moment; + dateMoment: moment.Moment; + timeMoment: moment.Moment; } export class ExpirationInput extends React.Component { - private _earliestPickableMoment: moment.Moment; - constructor(props: ExpirationInputProps) { - super(props); - // Set the earliest pickable date to today at 00:00, so users can only pick the current or later dates - this._earliestPickableMoment = moment().startOf('day'); - const expirationMoment = utils.convertToMomentFromUnixTimestamp(props.orderExpiryTimestamp); - const initialOrderExpiryTimestamp = utils.initialOrderExpiryUnixTimestampSec(); - const didUserSetExpiry = !initialOrderExpiryTimestamp.eq(props.orderExpiryTimestamp); - this.state = { - dateMoment: didUserSetExpiry ? expirationMoment : undefined, - timeMoment: didUserSetExpiry ? expirationMoment : undefined, - }; - } - public render() { - const date = this.state.dateMoment ? this.state.dateMoment.toDate() : undefined; - const time = this.state.timeMoment ? this.state.timeMoment.toDate() : undefined; - return ( -
-
- -
- -
-
-
- -
- -
-
-
- -
-
- ); - } - private _shouldDisableDate(date: Date): boolean { - return moment(date) - .startOf('day') - .isBefore(this._earliestPickableMoment); - } - private _clearDates() { - this.setState({ - dateMoment: undefined, - timeMoment: undefined, - }); - const defaultDateTime = utils.initialOrderExpiryUnixTimestampSec(); - this.props.updateOrderExpiry(defaultDateTime); - } - private _onDateChanged(e: any, date: Date) { - const dateMoment = moment(date); - this.setState({ - dateMoment, - }); - const timestamp = utils.convertToUnixTimestampSeconds(dateMoment, this.state.timeMoment); - this.props.updateOrderExpiry(timestamp); - } - private _onTimeChanged(e: any, time: Date) { - const timeMoment = moment(time); - this.setState({ - timeMoment, - }); - const dateMoment = _.isUndefined(this.state.dateMoment) ? moment() : this.state.dateMoment; - const timestamp = utils.convertToUnixTimestampSeconds(dateMoment, timeMoment); - this.props.updateOrderExpiry(timestamp); - } + private _earliestPickableMoment: moment.Moment; + constructor(props: ExpirationInputProps) { + super(props); + // Set the earliest pickable date to today at 00:00, so users can only pick the current or later dates + this._earliestPickableMoment = moment().startOf('day'); + const expirationMoment = utils.convertToMomentFromUnixTimestamp(props.orderExpiryTimestamp); + const initialOrderExpiryTimestamp = utils.initialOrderExpiryUnixTimestampSec(); + const didUserSetExpiry = !initialOrderExpiryTimestamp.eq(props.orderExpiryTimestamp); + this.state = { + dateMoment: didUserSetExpiry ? expirationMoment : undefined, + timeMoment: didUserSetExpiry ? expirationMoment : undefined, + }; + } + public render() { + const date = this.state.dateMoment ? this.state.dateMoment.toDate() : undefined; + const time = this.state.timeMoment ? this.state.timeMoment.toDate() : undefined; + return ( +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+
+ ); + } + private _shouldDisableDate(date: Date): boolean { + return moment(date) + .startOf('day') + .isBefore(this._earliestPickableMoment); + } + private _clearDates() { + this.setState({ + dateMoment: undefined, + timeMoment: undefined, + }); + const defaultDateTime = utils.initialOrderExpiryUnixTimestampSec(); + this.props.updateOrderExpiry(defaultDateTime); + } + private _onDateChanged(e: any, date: Date) { + const dateMoment = moment(date); + this.setState({ + dateMoment, + }); + const timestamp = utils.convertToUnixTimestampSeconds(dateMoment, this.state.timeMoment); + this.props.updateOrderExpiry(timestamp); + } + private _onTimeChanged(e: any, time: Date) { + const timeMoment = moment(time); + this.setState({ + timeMoment, + }); + const dateMoment = _.isUndefined(this.state.dateMoment) ? moment() : this.state.dateMoment; + const timestamp = utils.convertToUnixTimestampSeconds(dateMoment, timeMoment); + this.props.updateOrderExpiry(timestamp); + } } diff --git a/packages/website/ts/components/inputs/hash_input.tsx b/packages/website/ts/components/inputs/hash_input.tsx index 36d7e6140..5a3d34fe6 100644 --- a/packages/website/ts/components/inputs/hash_input.tsx +++ b/packages/website/ts/components/inputs/hash_input.tsx @@ -8,55 +8,55 @@ import { HashData, Styles } from 'ts/types'; import { constants } from 'ts/utils/constants'; const styles: Styles = { - textField: { - overflow: 'hidden', - paddingTop: 8, - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - }, + textField: { + overflow: 'hidden', + paddingTop: 8, + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + }, }; interface HashInputProps { - blockchain: Blockchain; - blockchainIsLoaded: boolean; - hashData: HashData; - label: string; + blockchain: Blockchain; + blockchainIsLoaded: boolean; + hashData: HashData; + label: string; } interface HashInputState {} export class HashInput extends React.Component { - public render() { - const msgHashHex = this.props.blockchainIsLoaded ? this._generateMessageHashHex() : ''; - return ( -
- -
- {msgHashHex} -
-
- {msgHashHex} -
- ); - } - private _generateMessageHashHex() { - const exchangeContractAddress = this.props.blockchain.getExchangeContractAddressIfExists(); - const hashData = this.props.hashData; - const order: Order = { - exchangeContractAddress, - expirationUnixTimestampSec: hashData.orderExpiryTimestamp, - feeRecipient: hashData.feeRecipientAddress, - maker: _.isEmpty(hashData.orderMakerAddress) ? constants.NULL_ADDRESS : hashData.orderMakerAddress, - makerFee: hashData.makerFee, - makerTokenAddress: hashData.depositTokenContractAddr, - makerTokenAmount: hashData.depositAmount, - salt: hashData.orderSalt, - taker: hashData.orderTakerAddress, - takerFee: hashData.takerFee, - takerTokenAddress: hashData.receiveTokenContractAddr, - takerTokenAmount: hashData.receiveAmount, - }; - const orderHash = ZeroEx.getOrderHashHex(order); - return orderHash; - } + public render() { + const msgHashHex = this.props.blockchainIsLoaded ? this._generateMessageHashHex() : ''; + return ( +
+ +
+ {msgHashHex} +
+
+ {msgHashHex} +
+ ); + } + private _generateMessageHashHex() { + const exchangeContractAddress = this.props.blockchain.getExchangeContractAddressIfExists(); + const hashData = this.props.hashData; + const order: Order = { + exchangeContractAddress, + expirationUnixTimestampSec: hashData.orderExpiryTimestamp, + feeRecipient: hashData.feeRecipientAddress, + maker: _.isEmpty(hashData.orderMakerAddress) ? constants.NULL_ADDRESS : hashData.orderMakerAddress, + makerFee: hashData.makerFee, + makerTokenAddress: hashData.depositTokenContractAddr, + makerTokenAmount: hashData.depositAmount, + salt: hashData.orderSalt, + taker: hashData.orderTakerAddress, + takerFee: hashData.takerFee, + takerTokenAddress: hashData.receiveTokenContractAddr, + takerTokenAmount: hashData.receiveAmount, + }; + const orderHash = ZeroEx.getOrderHashHex(order); + return orderHash; + } } diff --git a/packages/website/ts/components/inputs/identicon_address_input.tsx b/packages/website/ts/components/inputs/identicon_address_input.tsx index f14cb4e9c..4cf9af64d 100644 --- a/packages/website/ts/components/inputs/identicon_address_input.tsx +++ b/packages/website/ts/components/inputs/identicon_address_input.tsx @@ -6,48 +6,48 @@ import { InputLabel } from 'ts/components/ui/input_label'; import { RequiredLabel } from 'ts/components/ui/required_label'; interface IdenticonAddressInputProps { - initialAddress: string; - isRequired?: boolean; - label: string; - updateOrderAddress: (address?: string) => void; + initialAddress: string; + isRequired?: boolean; + label: string; + updateOrderAddress: (address?: string) => void; } interface IdenticonAddressInputState { - address: string; + address: string; } export class IdenticonAddressInput extends React.Component { - constructor(props: IdenticonAddressInputProps) { - super(props); - this.state = { - address: props.initialAddress, - }; - } - public render() { - const label = this.props.isRequired ? : this.props.label; - return ( -
- -
-
- -
-
- -
-
-
- ); - } - private _updateAddress(address?: string): void { - this.setState({ - address, - }); - this.props.updateOrderAddress(address); - } + constructor(props: IdenticonAddressInputProps) { + super(props); + this.state = { + address: props.initialAddress, + }; + } + public render() { + const label = this.props.isRequired ? : this.props.label; + return ( +
+ +
+
+ +
+
+ +
+
+
+ ); + } + private _updateAddress(address?: string): void { + this.setState({ + address, + }); + this.props.updateOrderAddress(address); + } } diff --git a/packages/website/ts/components/inputs/token_amount_input.tsx b/packages/website/ts/components/inputs/token_amount_input.tsx index 0a71b2c00..63966d759 100644 --- a/packages/website/ts/components/inputs/token_amount_input.tsx +++ b/packages/website/ts/components/inputs/token_amount_input.tsx @@ -8,63 +8,63 @@ import { InputErrMsg, Token, TokenState, ValidatedBigNumberCallback, WebsitePath import { colors } from 'ts/utils/colors'; interface TokenAmountInputProps { - token: Token; - tokenState: TokenState; - label?: string; - amount?: BigNumber; - shouldShowIncompleteErrs: boolean; - shouldCheckBalance: boolean; - shouldCheckAllowance: boolean; - onChange: ValidatedBigNumberCallback; - onVisitBalancesPageClick?: () => void; + token: Token; + tokenState: TokenState; + label?: string; + amount?: BigNumber; + shouldShowIncompleteErrs: boolean; + shouldCheckBalance: boolean; + shouldCheckAllowance: boolean; + onChange: ValidatedBigNumberCallback; + onVisitBalancesPageClick?: () => void; } interface TokenAmountInputState {} export class TokenAmountInput extends React.Component { - public render() { - const amount = this.props.amount - ? ZeroEx.toUnitAmount(this.props.amount, this.props.token.decimals) - : undefined; - const hasLabel = !_.isUndefined(this.props.label); - return ( -
- -
{this.props.token.symbol}
-
- ); - } - private _onChange(isValid: boolean, amount?: BigNumber) { - let baseUnitAmount; - if (!_.isUndefined(amount)) { - baseUnitAmount = ZeroEx.toBaseUnitAmount(amount, this.props.token.decimals); - } - this.props.onChange(isValid, baseUnitAmount); - } - private _validate(amount: BigNumber): InputErrMsg { - if (this.props.shouldCheckAllowance && amount.gt(this.props.tokenState.allowance)) { - return ( - - Insufficient allowance.{' '} - - Set allowance - - - ); - } else { - return undefined; - } - } + public render() { + const amount = this.props.amount + ? ZeroEx.toUnitAmount(this.props.amount, this.props.token.decimals) + : undefined; + const hasLabel = !_.isUndefined(this.props.label); + return ( +
+ +
{this.props.token.symbol}
+
+ ); + } + private _onChange(isValid: boolean, amount?: BigNumber) { + let baseUnitAmount; + if (!_.isUndefined(amount)) { + baseUnitAmount = ZeroEx.toBaseUnitAmount(amount, this.props.token.decimals); + } + this.props.onChange(isValid, baseUnitAmount); + } + private _validate(amount: BigNumber): InputErrMsg { + if (this.props.shouldCheckAllowance && amount.gt(this.props.tokenState.allowance)) { + return ( + + Insufficient allowance.{' '} + + Set allowance + + + ); + } else { + return undefined; + } + } } diff --git a/packages/website/ts/components/inputs/token_input.tsx b/packages/website/ts/components/inputs/token_input.tsx index 3aceacb22..5df19b28c 100644 --- a/packages/website/ts/components/inputs/token_input.tsx +++ b/packages/website/ts/components/inputs/token_input.tsx @@ -12,93 +12,93 @@ import { colors } from 'ts/utils/colors'; const TOKEN_ICON_DIMENSION = 80; interface TokenInputProps { - blockchain: Blockchain; - blockchainErr: BlockchainErrs; - dispatcher: Dispatcher; - label: string; - side: Side; - networkId: number; - assetToken: AssetToken; - updateChosenAssetToken: (side: Side, token: AssetToken) => void; - tokenByAddress: TokenByAddress; - userAddress: string; + blockchain: Blockchain; + blockchainErr: BlockchainErrs; + dispatcher: Dispatcher; + label: string; + side: Side; + networkId: number; + assetToken: AssetToken; + updateChosenAssetToken: (side: Side, token: AssetToken) => void; + tokenByAddress: TokenByAddress; + userAddress: string; } interface TokenInputState { - isHoveringIcon: boolean; - isPickerOpen: boolean; - trackCandidateTokenIfExists?: Token; + isHoveringIcon: boolean; + isPickerOpen: boolean; + trackCandidateTokenIfExists?: Token; } export class TokenInput extends React.Component { - constructor(props: TokenInputProps) { - super(props); - this.state = { - isHoveringIcon: false, - isPickerOpen: false, - }; - } - public render() { - const token = this.props.tokenByAddress[this.props.assetToken.address]; - const iconStyles = { - cursor: 'pointer', - opacity: this.state.isHoveringIcon ? 0.5 : 1, - }; - return ( -
-
- -
- -
- -
-
- {token.name} -
-
- -
- ); - } - private _onTokenChosen(tokenAddress: string) { - const assetToken: AssetToken = { - address: tokenAddress, - amount: this.props.assetToken.amount, - }; - this.props.updateChosenAssetToken(this.props.side, assetToken); - this.setState({ - isPickerOpen: false, - }); - } - private _onToggleHover(isHoveringIcon: boolean) { - this.setState({ - isHoveringIcon, - }); - } - private _onAssetClicked() { - if (this.props.blockchainErr !== BlockchainErrs.NoError) { - this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); - return; - } + constructor(props: TokenInputProps) { + super(props); + this.state = { + isHoveringIcon: false, + isPickerOpen: false, + }; + } + public render() { + const token = this.props.tokenByAddress[this.props.assetToken.address]; + const iconStyles = { + cursor: 'pointer', + opacity: this.state.isHoveringIcon ? 0.5 : 1, + }; + return ( +
+
+ +
+ +
+ +
+
+ {token.name} +
+
+ +
+ ); + } + private _onTokenChosen(tokenAddress: string) { + const assetToken: AssetToken = { + address: tokenAddress, + amount: this.props.assetToken.amount, + }; + this.props.updateChosenAssetToken(this.props.side, assetToken); + this.setState({ + isPickerOpen: false, + }); + } + private _onToggleHover(isHoveringIcon: boolean) { + this.setState({ + isHoveringIcon, + }); + } + private _onAssetClicked() { + if (this.props.blockchainErr !== BlockchainErrs.NoError) { + this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); + return; + } - this.setState({ - isPickerOpen: true, - }); - } + this.setState({ + isPickerOpen: true, + }); + } } diff --git a/packages/website/ts/components/order_json.tsx b/packages/website/ts/components/order_json.tsx index 1640a178e..1b6b32a04 100644 --- a/packages/website/ts/components/order_json.tsx +++ b/packages/website/ts/components/order_json.tsx @@ -11,172 +11,172 @@ import { errorReporter } from 'ts/utils/error_reporter'; import { utils } from 'ts/utils/utils'; interface OrderJSONProps { - exchangeContractIfExists: string; - orderExpiryTimestamp: BigNumber; - orderSignatureData: SignatureData; - orderTakerAddress: string; - orderMakerAddress: string; - orderSalt: BigNumber; - orderMakerFee: BigNumber; - orderTakerFee: BigNumber; - orderFeeRecipient: string; - networkId: number; - sideToAssetToken: SideToAssetToken; - tokenByAddress: TokenByAddress; + exchangeContractIfExists: string; + orderExpiryTimestamp: BigNumber; + orderSignatureData: SignatureData; + orderTakerAddress: string; + orderMakerAddress: string; + orderSalt: BigNumber; + orderMakerFee: BigNumber; + orderTakerFee: BigNumber; + orderFeeRecipient: string; + networkId: number; + sideToAssetToken: SideToAssetToken; + tokenByAddress: TokenByAddress; } interface OrderJSONState { - shareLink: string; + shareLink: string; } export class OrderJSON extends React.Component { - constructor(props: OrderJSONProps) { - super(props); - this.state = { - shareLink: '', - }; - // tslint:disable-next-line:no-floating-promises - this._setShareLinkAsync(); - } - public render() { - const order = utils.generateOrder( - this.props.networkId, - this.props.exchangeContractIfExists, - this.props.sideToAssetToken, - this.props.orderExpiryTimestamp, - this.props.orderTakerAddress, - this.props.orderMakerAddress, - this.props.orderMakerFee, - this.props.orderTakerFee, - this.props.orderFeeRecipient, - this.props.orderSignatureData, - this.props.tokenByAddress, - this.props.orderSalt, - ); - const orderJSON = JSON.stringify(order); - return ( -
-
- You have successfully generated and cryptographically signed an order! The following JSON contains - the order parameters and cryptographic signature that your counterparty will need to execute a trade - with you. -
-
-
- -
-
- - - -
-
Share your signed order!
-
-
- -
-
-
-
- -
-
- -
-
- -
-
-
-
- ); - } - private async _shareViaTwitterAsync() { - const tweetText = encodeURIComponent(`Fill my order using the 0x protocol: ${this.state.shareLink}`); - window.open(`https://twitter.com/intent/tweet?text=${tweetText}`, 'Share your order', 'width=500,height=400'); - } - private async _shareViaFacebook() { - (window as any).FB.ui( - { - display: 'popup', - href: this.state.shareLink, - method: 'share', - }, - _.noop, - ); - } - private async _shareViaEmailAsync() { - const encodedSubject = encodeURIComponent("Let's trade using the 0x protocol"); - const encodedBody = encodeURIComponent(`I generated an order with the 0x protocol. + constructor(props: OrderJSONProps) { + super(props); + this.state = { + shareLink: '', + }; + // tslint:disable-next-line:no-floating-promises + this._setShareLinkAsync(); + } + public render() { + const order = utils.generateOrder( + this.props.networkId, + this.props.exchangeContractIfExists, + this.props.sideToAssetToken, + this.props.orderExpiryTimestamp, + this.props.orderTakerAddress, + this.props.orderMakerAddress, + this.props.orderMakerFee, + this.props.orderTakerFee, + this.props.orderFeeRecipient, + this.props.orderSignatureData, + this.props.tokenByAddress, + this.props.orderSalt, + ); + const orderJSON = JSON.stringify(order); + return ( +
+
+ You have successfully generated and cryptographically signed an order! The following JSON contains + the order parameters and cryptographic signature that your counterparty will need to execute a trade + with you. +
+
+
+ +
+
+ + + +
+
Share your signed order!
+
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ ); + } + private async _shareViaTwitterAsync() { + const tweetText = encodeURIComponent(`Fill my order using the 0x protocol: ${this.state.shareLink}`); + window.open(`https://twitter.com/intent/tweet?text=${tweetText}`, 'Share your order', 'width=500,height=400'); + } + private async _shareViaFacebook() { + (window as any).FB.ui( + { + display: 'popup', + href: this.state.shareLink, + method: 'share', + }, + _.noop, + ); + } + private async _shareViaEmailAsync() { + const encodedSubject = encodeURIComponent("Let's trade using the 0x protocol"); + const encodedBody = encodeURIComponent(`I generated an order with the 0x protocol. You can see and fill it here: ${this.state.shareLink}`); - const mailToLink = `mailto:mail@example.org?subject=${encodedSubject}&body=${encodedBody}`; - window.open(mailToLink, '_blank'); - } - private async _setShareLinkAsync() { - const shareLink = await this._generateShareLinkAsync(); - this.setState({ - shareLink, - }); - } - private async _generateShareLinkAsync(): Promise { - const longUrl = encodeURIComponent(this._getOrderUrl()); - const bitlyRequestUrl = `${constants.URL_BITLY_API}/v3/shorten?access_token=${ - configs.BITLY_ACCESS_TOKEN - }&longUrl=${longUrl}`; - const response = await fetch(bitlyRequestUrl); - const responseBody = await response.text(); - const bodyObj = JSON.parse(responseBody); - if (response.status !== 200 || bodyObj.status_code !== 200) { - // TODO: Show error message in UI - utils.consoleLog(`Unexpected status code: ${response.status} -> ${responseBody}`); - await errorReporter.reportAsync(new Error(`Bitly returned non-200: ${JSON.stringify(response)}`)); - return ''; - } - return bodyObj.data.url; - } - private _getOrderUrl() { - const order = utils.generateOrder( - this.props.networkId, - this.props.exchangeContractIfExists, - this.props.sideToAssetToken, - this.props.orderExpiryTimestamp, - this.props.orderTakerAddress, - this.props.orderMakerAddress, - this.props.orderMakerFee, - this.props.orderTakerFee, - this.props.orderFeeRecipient, - this.props.orderSignatureData, - this.props.tokenByAddress, - this.props.orderSalt, - ); - const orderJSONString = JSON.stringify(order); - const orderUrl = `${configs.BASE_URL}${WebsitePaths.Portal}/fill?order=${orderJSONString}`; - return orderUrl; - } + const mailToLink = `mailto:mail@example.org?subject=${encodedSubject}&body=${encodedBody}`; + window.open(mailToLink, '_blank'); + } + private async _setShareLinkAsync() { + const shareLink = await this._generateShareLinkAsync(); + this.setState({ + shareLink, + }); + } + private async _generateShareLinkAsync(): Promise { + const longUrl = encodeURIComponent(this._getOrderUrl()); + const bitlyRequestUrl = `${constants.URL_BITLY_API}/v3/shorten?access_token=${ + configs.BITLY_ACCESS_TOKEN + }&longUrl=${longUrl}`; + const response = await fetch(bitlyRequestUrl); + const responseBody = await response.text(); + const bodyObj = JSON.parse(responseBody); + if (response.status !== 200 || bodyObj.status_code !== 200) { + // TODO: Show error message in UI + utils.consoleLog(`Unexpected status code: ${response.status} -> ${responseBody}`); + await errorReporter.reportAsync(new Error(`Bitly returned non-200: ${JSON.stringify(response)}`)); + return ''; + } + return bodyObj.data.url; + } + private _getOrderUrl() { + const order = utils.generateOrder( + this.props.networkId, + this.props.exchangeContractIfExists, + this.props.sideToAssetToken, + this.props.orderExpiryTimestamp, + this.props.orderTakerAddress, + this.props.orderMakerAddress, + this.props.orderMakerFee, + this.props.orderTakerFee, + this.props.orderFeeRecipient, + this.props.orderSignatureData, + this.props.tokenByAddress, + this.props.orderSalt, + ); + const orderJSONString = JSON.stringify(order); + const orderUrl = `${configs.BASE_URL}${WebsitePaths.Portal}/fill?order=${orderJSONString}`; + return orderUrl; + } } diff --git a/packages/website/ts/components/portal.tsx b/packages/website/ts/components/portal.tsx index e163a1fa2..e2e28e8b6 100644 --- a/packages/website/ts/components/portal.tsx +++ b/packages/website/ts/components/portal.tsx @@ -23,14 +23,14 @@ import { Dispatcher } from 'ts/redux/dispatcher'; import { orderSchema } from 'ts/schemas/order_schema'; import { SchemaValidator } from 'ts/schemas/validator'; import { - BlockchainErrs, - HashData, - Order, - ScreenWidths, - Token, - TokenByAddress, - TokenStateByAddress, - WebsitePaths, + BlockchainErrs, + HashData, + Order, + ScreenWidths, + Token, + TokenByAddress, + TokenStateByAddress, + WebsitePaths, } from 'ts/types'; import { colors } from 'ts/utils/colors'; import { configs } from 'ts/utils/configs'; @@ -42,320 +42,320 @@ const THROTTLE_TIMEOUT = 100; export interface PortalPassedProps {} export interface PortalAllProps { - blockchainErr: BlockchainErrs; - blockchainIsLoaded: boolean; - dispatcher: Dispatcher; - hashData: HashData; - networkId: number; - nodeVersion: string; - orderFillAmount: BigNumber; - screenWidth: ScreenWidths; - tokenByAddress: TokenByAddress; - tokenStateByAddress: TokenStateByAddress; - userEtherBalance: BigNumber; - userAddress: string; - shouldBlockchainErrDialogBeOpen: boolean; - userSuppliedOrderCache: Order; - location: Location; - flashMessage?: string | React.ReactNode; + blockchainErr: BlockchainErrs; + blockchainIsLoaded: boolean; + dispatcher: Dispatcher; + hashData: HashData; + networkId: number; + nodeVersion: string; + orderFillAmount: BigNumber; + screenWidth: ScreenWidths; + tokenByAddress: TokenByAddress; + tokenStateByAddress: TokenStateByAddress; + userEtherBalance: BigNumber; + userAddress: string; + shouldBlockchainErrDialogBeOpen: boolean; + userSuppliedOrderCache: Order; + location: Location; + flashMessage?: string | React.ReactNode; } interface PortalAllState { - prevNetworkId: number; - prevNodeVersion: string; - prevUserAddress: string; - prevPathname: string; - isDisclaimerDialogOpen: boolean; - isWethNoticeDialogOpen: boolean; + prevNetworkId: number; + prevNodeVersion: string; + prevUserAddress: string; + prevPathname: string; + isDisclaimerDialogOpen: boolean; + isWethNoticeDialogOpen: boolean; } export class Portal extends React.Component { - private _blockchain: Blockchain; - private _sharedOrderIfExists: Order; - private _throttledScreenWidthUpdate: () => void; - public static hasAlreadyDismissedWethNotice() { - const didDismissWethNotice = localStorage.getItemIfExists(constants.LOCAL_STORAGE_KEY_DISMISS_WETH_NOTICE); - const hasAlreadyDismissedWethNotice = !_.isUndefined(didDismissWethNotice) && !_.isEmpty(didDismissWethNotice); - return hasAlreadyDismissedWethNotice; - } - constructor(props: PortalAllProps) { - super(props); - this._sharedOrderIfExists = this._getSharedOrderIfExists(); - this._throttledScreenWidthUpdate = _.throttle(this._updateScreenWidth.bind(this), THROTTLE_TIMEOUT); + private _blockchain: Blockchain; + private _sharedOrderIfExists: Order; + private _throttledScreenWidthUpdate: () => void; + public static hasAlreadyDismissedWethNotice() { + const didDismissWethNotice = localStorage.getItemIfExists(constants.LOCAL_STORAGE_KEY_DISMISS_WETH_NOTICE); + const hasAlreadyDismissedWethNotice = !_.isUndefined(didDismissWethNotice) && !_.isEmpty(didDismissWethNotice); + return hasAlreadyDismissedWethNotice; + } + constructor(props: PortalAllProps) { + super(props); + this._sharedOrderIfExists = this._getSharedOrderIfExists(); + this._throttledScreenWidthUpdate = _.throttle(this._updateScreenWidth.bind(this), THROTTLE_TIMEOUT); - const isViewingBalances = _.includes(props.location.pathname, `${WebsitePaths.Portal}/balances`); - const hasAlreadyDismissedWethNotice = Portal.hasAlreadyDismissedWethNotice(); + const isViewingBalances = _.includes(props.location.pathname, `${WebsitePaths.Portal}/balances`); + const hasAlreadyDismissedWethNotice = Portal.hasAlreadyDismissedWethNotice(); - const didAcceptPortalDisclaimer = localStorage.getItemIfExists(constants.LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER); - const hasAcceptedDisclaimer = - !_.isUndefined(didAcceptPortalDisclaimer) && !_.isEmpty(didAcceptPortalDisclaimer); - this.state = { - prevNetworkId: this.props.networkId, - prevNodeVersion: this.props.nodeVersion, - prevUserAddress: this.props.userAddress, - prevPathname: this.props.location.pathname, - isDisclaimerDialogOpen: !hasAcceptedDisclaimer, - isWethNoticeDialogOpen: !hasAlreadyDismissedWethNotice && isViewingBalances, - }; - } - public componentDidMount() { - window.addEventListener('resize', this._throttledScreenWidthUpdate); - window.scrollTo(0, 0); - } - public componentWillMount() { - this._blockchain = new Blockchain(this.props.dispatcher); - } - public componentWillUnmount() { - this._blockchain.destroy(); - window.removeEventListener('resize', this._throttledScreenWidthUpdate); - // We re-set the entire redux state when the portal is unmounted so that when it is re-rendered - // the initialization process always occurs from the same base state. This helps avoid - // initialization inconsistencies (i.e While the portal was unrendered, the user might have - // become disconnected from their backing Ethereum node, changes user accounts, etc...) - this.props.dispatcher.resetState(); - } - public componentWillReceiveProps(nextProps: PortalAllProps) { - if (nextProps.networkId !== this.state.prevNetworkId) { - // tslint:disable-next-line:no-floating-promises - this._blockchain.networkIdUpdatedFireAndForgetAsync(nextProps.networkId); - this.setState({ - prevNetworkId: nextProps.networkId, - }); - } - if (nextProps.userAddress !== this.state.prevUserAddress) { - // tslint:disable-next-line:no-floating-promises - this._blockchain.userAddressUpdatedFireAndForgetAsync(nextProps.userAddress); - if (!_.isEmpty(nextProps.userAddress) && nextProps.blockchainIsLoaded) { - const tokens = _.values(nextProps.tokenByAddress); - // tslint:disable-next-line:no-floating-promises - this._updateBalanceAndAllowanceWithLoadingScreenAsync(tokens); - } - this.setState({ - prevUserAddress: nextProps.userAddress, - }); - } - if (nextProps.nodeVersion !== this.state.prevNodeVersion) { - // tslint:disable-next-line:no-floating-promises - this._blockchain.nodeVersionUpdatedFireAndForgetAsync(nextProps.nodeVersion); - } - if (nextProps.location.pathname !== this.state.prevPathname) { - const isViewingBalances = _.includes(nextProps.location.pathname, `${WebsitePaths.Portal}/balances`); - const hasAlreadyDismissedWethNotice = Portal.hasAlreadyDismissedWethNotice(); - this.setState({ - prevPathname: nextProps.location.pathname, - isWethNoticeDialogOpen: !hasAlreadyDismissedWethNotice && isViewingBalances, - }); - } - } - public render() { - const updateShouldBlockchainErrDialogBeOpen = this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen.bind( - this.props.dispatcher, - ); - const portalStyle: React.CSSProperties = { - minHeight: '100vh', - display: 'flex', - flexDirection: 'column', - justifyContent: 'space-between', - }; - const portalMenuContainerStyle: React.CSSProperties = { - overflow: 'hidden', - backgroundColor: colors.darkestGrey, - color: colors.white, - }; - return ( -
- - -
- - {!configs.IS_MAINNET_ENABLED && this.props.networkId === constants.NETWORK_ID_MAINNET ? ( -
-
Mainnet unavailable
-
- -
-
- 0x portal is currently unavailable on the Ethereum mainnet. -
To try it out, switch to the Kovan test network (networkId: 42).
-
Check back soon!
-
-
- ) : ( -
-
- -
-
-
- {this.props.blockchainIsLoaded ? ( - - - - - - - - ) : ( - - )} -
-
-
- )} -
- - - - -
-
-
- ); - } - private _renderEthWrapper() { - return ( - - ); - } - private _renderTradeHistory() { - return ( - - ); - } - private _renderTokenBalances() { - return ( - - ); - } - private _renderFillOrder(match: any, location: Location, history: History) { - const initialFillOrder = !_.isUndefined(this.props.userSuppliedOrderCache) - ? this.props.userSuppliedOrderCache - : this._sharedOrderIfExists; - return ( - - ); - } - private _renderGenerateOrderForm(match: any, location: Location, history: History) { - return ( - - ); - } - private _onPortalDisclaimerAccepted() { - localStorage.setItem(constants.LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER, 'set'); - this.setState({ - isDisclaimerDialogOpen: false, - }); - } - private _onWethNoticeAccepted() { - localStorage.setItem(constants.LOCAL_STORAGE_KEY_DISMISS_WETH_NOTICE, 'set'); - this.setState({ - 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 didAcceptPortalDisclaimer = localStorage.getItemIfExists(constants.LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER); + const hasAcceptedDisclaimer = + !_.isUndefined(didAcceptPortalDisclaimer) && !_.isEmpty(didAcceptPortalDisclaimer); + this.state = { + prevNetworkId: this.props.networkId, + prevNodeVersion: this.props.nodeVersion, + prevUserAddress: this.props.userAddress, + prevPathname: this.props.location.pathname, + isDisclaimerDialogOpen: !hasAcceptedDisclaimer, + isWethNoticeDialogOpen: !hasAlreadyDismissedWethNotice && isViewingBalances, + }; + } + public componentDidMount() { + window.addEventListener('resize', this._throttledScreenWidthUpdate); + window.scrollTo(0, 0); + } + public componentWillMount() { + this._blockchain = new Blockchain(this.props.dispatcher); + } + public componentWillUnmount() { + this._blockchain.destroy(); + window.removeEventListener('resize', this._throttledScreenWidthUpdate); + // We re-set the entire redux state when the portal is unmounted so that when it is re-rendered + // the initialization process always occurs from the same base state. This helps avoid + // initialization inconsistencies (i.e While the portal was unrendered, the user might have + // become disconnected from their backing Ethereum node, changes user accounts, etc...) + this.props.dispatcher.resetState(); + } + public componentWillReceiveProps(nextProps: PortalAllProps) { + if (nextProps.networkId !== this.state.prevNetworkId) { + // tslint:disable-next-line:no-floating-promises + this._blockchain.networkIdUpdatedFireAndForgetAsync(nextProps.networkId); + this.setState({ + prevNetworkId: nextProps.networkId, + }); + } + if (nextProps.userAddress !== this.state.prevUserAddress) { + // tslint:disable-next-line:no-floating-promises + this._blockchain.userAddressUpdatedFireAndForgetAsync(nextProps.userAddress); + if (!_.isEmpty(nextProps.userAddress) && nextProps.blockchainIsLoaded) { + const tokens = _.values(nextProps.tokenByAddress); + // tslint:disable-next-line:no-floating-promises + this._updateBalanceAndAllowanceWithLoadingScreenAsync(tokens); + } + this.setState({ + prevUserAddress: nextProps.userAddress, + }); + } + if (nextProps.nodeVersion !== this.state.prevNodeVersion) { + // tslint:disable-next-line:no-floating-promises + this._blockchain.nodeVersionUpdatedFireAndForgetAsync(nextProps.nodeVersion); + } + if (nextProps.location.pathname !== this.state.prevPathname) { + const isViewingBalances = _.includes(nextProps.location.pathname, `${WebsitePaths.Portal}/balances`); + const hasAlreadyDismissedWethNotice = Portal.hasAlreadyDismissedWethNotice(); + this.setState({ + prevPathname: nextProps.location.pathname, + isWethNoticeDialogOpen: !hasAlreadyDismissedWethNotice && isViewingBalances, + }); + } + } + public render() { + const updateShouldBlockchainErrDialogBeOpen = this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen.bind( + this.props.dispatcher, + ); + const portalStyle: React.CSSProperties = { + minHeight: '100vh', + display: 'flex', + flexDirection: 'column', + justifyContent: 'space-between', + }; + const portalMenuContainerStyle: React.CSSProperties = { + overflow: 'hidden', + backgroundColor: colors.darkestGrey, + color: colors.white, + }; + return ( +
+ + +
+ + {!configs.IS_MAINNET_ENABLED && this.props.networkId === constants.NETWORK_ID_MAINNET ? ( +
+
Mainnet unavailable
+
+ +
+
+ 0x portal is currently unavailable on the Ethereum mainnet. +
To try it out, switch to the Kovan test network (networkId: 42).
+
Check back soon!
+
+
+ ) : ( +
+
+ +
+
+
+ {this.props.blockchainIsLoaded ? ( + + + + + + + + ) : ( + + )} +
+
+
+ )} +
+ + + + +
+
+
+ ); + } + private _renderEthWrapper() { + return ( + + ); + } + private _renderTradeHistory() { + return ( + + ); + } + private _renderTokenBalances() { + return ( + + ); + } + private _renderFillOrder(match: any, location: Location, history: History) { + const initialFillOrder = !_.isUndefined(this.props.userSuppliedOrderCache) + ? this.props.userSuppliedOrderCache + : this._sharedOrderIfExists; + return ( + + ); + } + private _renderGenerateOrderForm(match: any, location: Location, history: History) { + return ( + + ); + } + private _onPortalDisclaimerAccepted() { + localStorage.setItem(constants.LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER, 'set'); + this.setState({ + isDisclaimerDialogOpen: false, + }); + } + private _onWethNoticeAccepted() { + localStorage.setItem(constants.LOCAL_STORAGE_KEY_DISMISS_WETH_NOTICE, 'set'); + this.setState({ + 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 validator = new SchemaValidator(); - const order = JSON.parse(decodeURIComponent(orderPair[1])); - const validationResult = validator.validate(order, orderSchema); - if (validationResult.errors.length > 0) { - utils.consoleLog(`Invalid shared order: ${validationResult.errors}`); - return undefined; - } - return order; - } - private _updateScreenWidth() { - const newScreenWidth = utils.getScreenWidth(); - this.props.dispatcher.updateScreenWidth(newScreenWidth); - } - private async _updateBalanceAndAllowanceWithLoadingScreenAsync(tokens: Token[]) { - this.props.dispatcher.updateBlockchainIsLoaded(false); - await this._blockchain.updateTokenBalancesAndAllowancesAsync(tokens); - this.props.dispatcher.updateBlockchainIsLoaded(true); - } + const validator = new SchemaValidator(); + const order = JSON.parse(decodeURIComponent(orderPair[1])); + const validationResult = validator.validate(order, orderSchema); + if (validationResult.errors.length > 0) { + utils.consoleLog(`Invalid shared order: ${validationResult.errors}`); + return undefined; + } + return order; + } + private _updateScreenWidth() { + const newScreenWidth = utils.getScreenWidth(); + this.props.dispatcher.updateScreenWidth(newScreenWidth); + } + private async _updateBalanceAndAllowanceWithLoadingScreenAsync(tokens: Token[]) { + this.props.dispatcher.updateBlockchainIsLoaded(false); + await this._blockchain.updateTokenBalancesAndAllowancesAsync(tokens); + this.props.dispatcher.updateBlockchainIsLoaded(true); + } } diff --git a/packages/website/ts/components/portal_menu.tsx b/packages/website/ts/components/portal_menu.tsx index b025f527e..a2f9340c8 100644 --- a/packages/website/ts/components/portal_menu.tsx +++ b/packages/website/ts/components/portal_menu.tsx @@ -4,70 +4,70 @@ import { MenuItem } from 'ts/components/ui/menu_item'; import { WebsitePaths } from 'ts/types'; export interface PortalMenuProps { - menuItemStyle: React.CSSProperties; - onClick?: () => void; + menuItemStyle: React.CSSProperties; + onClick?: () => void; } interface PortalMenuState {} export class PortalMenu extends React.Component { - public static defaultProps: Partial = { - onClick: _.noop, - }; - public render() { - return ( -
- - {this._renderMenuItemWithIcon('Generate order', 'zmdi-arrow-right-top')} - - - {this._renderMenuItemWithIcon('Fill order', 'zmdi-arrow-left-bottom')} - - - {this._renderMenuItemWithIcon('Balances', 'zmdi-balance-wallet')} - - - {this._renderMenuItemWithIcon('Trade history', 'zmdi-format-list-bulleted')} - - - {this._renderMenuItemWithIcon('Wrap ETH', 'zmdi-circle-o')} - -
- ); - } - private _renderMenuItemWithIcon(title: string, iconName: string) { - return ( -
-
- -
-
{title}
-
- ); - } + public static defaultProps: Partial = { + onClick: _.noop, + }; + public render() { + return ( +
+ + {this._renderMenuItemWithIcon('Generate order', 'zmdi-arrow-right-top')} + + + {this._renderMenuItemWithIcon('Fill order', 'zmdi-arrow-left-bottom')} + + + {this._renderMenuItemWithIcon('Balances', 'zmdi-balance-wallet')} + + + {this._renderMenuItemWithIcon('Trade history', 'zmdi-format-list-bulleted')} + + + {this._renderMenuItemWithIcon('Wrap ETH', 'zmdi-circle-o')} + +
+ ); + } + private _renderMenuItemWithIcon(title: string, iconName: string) { + return ( +
+
+ +
+
{title}
+
+ ); + } } diff --git a/packages/website/ts/components/send_button.tsx b/packages/website/ts/components/send_button.tsx index f97d9250b..f94ec346a 100644 --- a/packages/website/ts/components/send_button.tsx +++ b/packages/website/ts/components/send_button.tsx @@ -10,78 +10,78 @@ import { errorReporter } from 'ts/utils/error_reporter'; import { utils } from 'ts/utils/utils'; interface SendButtonProps { - token: Token; - tokenState: TokenState; - dispatcher: Dispatcher; - blockchain: Blockchain; - onError: () => void; + token: Token; + tokenState: TokenState; + dispatcher: Dispatcher; + blockchain: Blockchain; + onError: () => void; } interface SendButtonState { - isSendDialogVisible: boolean; - isSending: boolean; + isSendDialogVisible: boolean; + isSending: boolean; } export class SendButton extends React.Component { - public constructor(props: SendButtonProps) { - super(props); - this.state = { - isSendDialogVisible: false, - isSending: false, - }; - } - public render() { - const labelStyle = this.state.isSending ? { fontSize: 10 } : {}; - return ( -
- - -
- ); - } - private _toggleSendDialog() { - this.setState({ - isSendDialogVisible: !this.state.isSendDialogVisible, - }); - } - private async _onSendAmountSelectedAsync(recipient: string, value: BigNumber) { - this.setState({ - isSending: true, - }); - this._toggleSendDialog(); - const token = this.props.token; - const tokenState = this.props.tokenState; - let balance = tokenState.balance; - try { - await this.props.blockchain.transferAsync(token, recipient, value); - balance = balance.minus(value); - this.props.dispatcher.replaceTokenBalanceByAddress(token.address, balance); - } catch (err) { - const errMsg = `${err}`; - if (_.includes(errMsg, BlockchainCallErrs.UserHasNoAssociatedAddresses)) { - this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); - return; - } else if (!_.includes(errMsg, 'User denied transaction')) { - utils.consoleLog(`Unexpected error encountered: ${err}`); - utils.consoleLog(err.stack); - this.props.onError(); - await errorReporter.reportAsync(err); - } - } - this.setState({ - isSending: false, - }); - } + public constructor(props: SendButtonProps) { + super(props); + this.state = { + isSendDialogVisible: false, + isSending: false, + }; + } + public render() { + const labelStyle = this.state.isSending ? { fontSize: 10 } : {}; + return ( +
+ + +
+ ); + } + private _toggleSendDialog() { + this.setState({ + isSendDialogVisible: !this.state.isSendDialogVisible, + }); + } + private async _onSendAmountSelectedAsync(recipient: string, value: BigNumber) { + this.setState({ + isSending: true, + }); + this._toggleSendDialog(); + const token = this.props.token; + const tokenState = this.props.tokenState; + let balance = tokenState.balance; + try { + await this.props.blockchain.transferAsync(token, recipient, value); + balance = balance.minus(value); + this.props.dispatcher.replaceTokenBalanceByAddress(token.address, balance); + } catch (err) { + const errMsg = `${err}`; + if (_.includes(errMsg, BlockchainCallErrs.UserHasNoAssociatedAddresses)) { + this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); + return; + } else if (!_.includes(errMsg, 'User denied transaction')) { + utils.consoleLog(`Unexpected error encountered: ${err}`); + utils.consoleLog(err.stack); + this.props.onError(); + await errorReporter.reportAsync(err); + } + } + this.setState({ + isSending: false, + }); + } } diff --git a/packages/website/ts/components/token_balances.tsx b/packages/website/ts/components/token_balances.tsx index 480652c34..2cef413c7 100644 --- a/packages/website/ts/components/token_balances.tsx +++ b/packages/website/ts/components/token_balances.tsx @@ -23,16 +23,16 @@ import { TokenIcon } from 'ts/components/ui/token_icon'; import { trackedTokenStorage } from 'ts/local_storage/tracked_token_storage'; import { Dispatcher } from 'ts/redux/dispatcher'; import { - BalanceErrs, - BlockchainCallErrs, - BlockchainErrs, - EtherscanLinkSuffixes, - ScreenWidths, - Styles, - Token, - TokenByAddress, - TokenStateByAddress, - TokenVisibility, + BalanceErrs, + BlockchainCallErrs, + BlockchainErrs, + EtherscanLinkSuffixes, + ScreenWidths, + Styles, + Token, + TokenByAddress, + TokenStateByAddress, + TokenVisibility, } from 'ts/types'; import { colors } from 'ts/utils/colors'; import { configs } from 'ts/utils/configs'; @@ -53,554 +53,554 @@ const TOKEN_COL_SPAN_LG = 2; const TOKEN_COL_SPAN_SM = 1; const styles: Styles = { - bgColor: { - backgroundColor: colors.grey50, - }, + bgColor: { + backgroundColor: colors.grey50, + }, }; interface TokenBalancesProps { - blockchain: Blockchain; - blockchainErr: BlockchainErrs; - blockchainIsLoaded: boolean; - dispatcher: Dispatcher; - screenWidth: ScreenWidths; - tokenByAddress: TokenByAddress; - tokenStateByAddress: TokenStateByAddress; - userAddress: string; - userEtherBalance: BigNumber; - networkId: number; + blockchain: Blockchain; + blockchainErr: BlockchainErrs; + blockchainIsLoaded: boolean; + dispatcher: Dispatcher; + screenWidth: ScreenWidths; + tokenByAddress: TokenByAddress; + tokenStateByAddress: TokenStateByAddress; + userAddress: string; + userEtherBalance: BigNumber; + networkId: number; } interface TokenBalancesState { - errorType: BalanceErrs; - isBalanceSpinnerVisible: boolean; - isDharmaDialogVisible: boolean; - isZRXSpinnerVisible: boolean; - currentZrxBalance?: BigNumber; - isTokenPickerOpen: boolean; - isAddingToken: boolean; + errorType: BalanceErrs; + isBalanceSpinnerVisible: boolean; + isDharmaDialogVisible: boolean; + isZRXSpinnerVisible: boolean; + currentZrxBalance?: BigNumber; + isTokenPickerOpen: boolean; + isAddingToken: boolean; } export class TokenBalances extends React.Component { - public constructor(props: TokenBalancesProps) { - super(props); - this.state = { - errorType: undefined, - isBalanceSpinnerVisible: false, - isZRXSpinnerVisible: false, - isDharmaDialogVisible: DharmaLoanFrame.isAuthTokenPresent(), - isTokenPickerOpen: false, - isAddingToken: false, - }; - } - public componentWillReceiveProps(nextProps: TokenBalancesProps) { - if (nextProps.userEtherBalance !== this.props.userEtherBalance) { - if (this.state.isBalanceSpinnerVisible) { - const receivedAmount = nextProps.userEtherBalance.minus(this.props.userEtherBalance); - this.props.dispatcher.showFlashMessage(`Received ${receivedAmount.toString(10)} Kovan Ether`); - } - this.setState({ - isBalanceSpinnerVisible: false, - }); - } - const nextZrxToken = _.find(_.values(nextProps.tokenByAddress), t => t.symbol === ZRX_TOKEN_SYMBOL); - const nextZrxTokenBalance = nextProps.tokenStateByAddress[nextZrxToken.address].balance; - if (!_.isUndefined(this.state.currentZrxBalance) && !nextZrxTokenBalance.eq(this.state.currentZrxBalance)) { - if (this.state.isZRXSpinnerVisible) { - const receivedAmount = nextZrxTokenBalance.minus(this.state.currentZrxBalance); - const receiveAmountInUnits = ZeroEx.toUnitAmount(receivedAmount, constants.DECIMAL_PLACES_ZRX); - this.props.dispatcher.showFlashMessage(`Received ${receiveAmountInUnits.toString(10)} Kovan ZRX`); - } - this.setState({ - isZRXSpinnerVisible: false, - currentZrxBalance: undefined, - }); - } - } - public componentDidMount() { - window.scrollTo(0, 0); - } - public render() { - const errorDialogActions = [ - , - ]; - const dharmaDialogActions = [ - , - ]; - const isTestNetwork = this.props.networkId === constants.NETWORK_ID_TESTNET; - const dharmaButtonColumnStyle = { - paddingLeft: 3, - display: isTestNetwork ? 'table-cell' : 'none', - }; - const stubColumnStyle = { - display: isTestNetwork ? 'none' : 'table-cell', - }; - const allTokenRowHeight = _.size(this.props.tokenByAddress) * TOKEN_TABLE_ROW_HEIGHT; - const tokenTableHeight = - allTokenRowHeight < MAX_TOKEN_TABLE_HEIGHT ? allTokenRowHeight : MAX_TOKEN_TABLE_HEIGHT; - const isSmallScreen = this.props.screenWidth === ScreenWidths.Sm; - const tokenColSpan = isSmallScreen ? TOKEN_COL_SPAN_SM : TOKEN_COL_SPAN_LG; - const dharmaLoanExplanation = - 'If you need access to larger amounts of ether,
\ + public constructor(props: TokenBalancesProps) { + super(props); + this.state = { + errorType: undefined, + isBalanceSpinnerVisible: false, + isZRXSpinnerVisible: false, + isDharmaDialogVisible: DharmaLoanFrame.isAuthTokenPresent(), + isTokenPickerOpen: false, + isAddingToken: false, + }; + } + public componentWillReceiveProps(nextProps: TokenBalancesProps) { + if (nextProps.userEtherBalance !== this.props.userEtherBalance) { + if (this.state.isBalanceSpinnerVisible) { + const receivedAmount = nextProps.userEtherBalance.minus(this.props.userEtherBalance); + this.props.dispatcher.showFlashMessage(`Received ${receivedAmount.toString(10)} Kovan Ether`); + } + this.setState({ + isBalanceSpinnerVisible: false, + }); + } + const nextZrxToken = _.find(_.values(nextProps.tokenByAddress), t => t.symbol === ZRX_TOKEN_SYMBOL); + const nextZrxTokenBalance = nextProps.tokenStateByAddress[nextZrxToken.address].balance; + if (!_.isUndefined(this.state.currentZrxBalance) && !nextZrxTokenBalance.eq(this.state.currentZrxBalance)) { + if (this.state.isZRXSpinnerVisible) { + const receivedAmount = nextZrxTokenBalance.minus(this.state.currentZrxBalance); + const receiveAmountInUnits = ZeroEx.toUnitAmount(receivedAmount, constants.DECIMAL_PLACES_ZRX); + this.props.dispatcher.showFlashMessage(`Received ${receiveAmountInUnits.toString(10)} Kovan ZRX`); + } + this.setState({ + isZRXSpinnerVisible: false, + currentZrxBalance: undefined, + }); + } + } + public componentDidMount() { + window.scrollTo(0, 0); + } + public render() { + const errorDialogActions = [ + , + ]; + const dharmaDialogActions = [ + , + ]; + const isTestNetwork = this.props.networkId === constants.NETWORK_ID_TESTNET; + const dharmaButtonColumnStyle = { + paddingLeft: 3, + display: isTestNetwork ? 'table-cell' : 'none', + }; + const stubColumnStyle = { + display: isTestNetwork ? 'none' : 'table-cell', + }; + const allTokenRowHeight = _.size(this.props.tokenByAddress) * TOKEN_TABLE_ROW_HEIGHT; + const tokenTableHeight = + allTokenRowHeight < MAX_TOKEN_TABLE_HEIGHT ? allTokenRowHeight : MAX_TOKEN_TABLE_HEIGHT; + const isSmallScreen = this.props.screenWidth === ScreenWidths.Sm; + const tokenColSpan = isSmallScreen ? TOKEN_COL_SPAN_SM : TOKEN_COL_SPAN_LG; + const dharmaLoanExplanation = + 'If you need access to larger amounts of ether,
\ you can request a loan from the Dharma Loan
\ network. Your loan should be funded in 5
\ minutes or less.'; - const allowanceExplanation = - '0x smart contracts require access to your
\ + const allowanceExplanation = + '0x smart contracts require access to your
\ token balances in order to execute trades.
\ Toggling sets an allowance for the
\ smart contract so you can start trading that token.'; - return ( -
-

{isTestNetwork ? 'Test ether' : 'Ether'}

- -
- {isTestNetwork - ? 'In order to try out the 0x Portal Dapp, request some test ether to pay for \ + return ( +
+

{isTestNetwork ? 'Test ether' : 'Ether'}

+ +
+ {isTestNetwork + ? 'In order to try out the 0x Portal Dapp, request some test ether to pay for \ gas costs. It might take a bit of time for the test ether to show up.' - : 'Ether must be converted to Ether Tokens in order to be tradable via 0x. \ + : 'Ether must be converted to Ether Tokens in order to be tradable via 0x. \ You can convert between Ether and Ether Tokens from the "Wrap ETH" tab.'} -
- - - - Currency - Balance - - {isTestNetwork && ( - - {isSmallScreen ? 'Faucet' : 'Request from faucet'} - - )} - {isTestNetwork && ( - - {isSmallScreen ? 'Loan' : 'Request Dharma loan'} - - - )} - - - - - - - - - {this.props.userEtherBalance.toFixed(PRECISION)} ETH - {this.state.isBalanceSpinnerVisible && ( - - - - )} - - - {isTestNetwork && ( - - - - )} - {isTestNetwork && ( - - - - )} - - -
-
-
-

{isTestNetwork ? 'Test tokens' : 'Tokens'}

-
-
- - - -
-
- - - -
-
- -
- {isTestNetwork - ? "Mint some test tokens you'd like to use to generate or fill an order using 0x." - : "Set trading permissions for a token you'd like to start trading."} -
- - - - Token - Balance - -
Allowance
- -
- Action - {this.props.screenWidth !== ScreenWidths.Sm && Send} -
-
- {this._renderTokenTableRows()} -
- - {this._renderErrorDialogBody()} - - - {this._renderDharmaLoanFrame()} - - -
- ); - } - private _renderTokenTableRows() { - if (!this.props.blockchainIsLoaded || this.props.blockchainErr !== BlockchainErrs.NoError) { - return ''; - } - const isSmallScreen = this.props.screenWidth === ScreenWidths.Sm; - const tokenColSpan = isSmallScreen ? TOKEN_COL_SPAN_SM : TOKEN_COL_SPAN_LG; - const actionPaddingX = isSmallScreen ? 2 : 24; - const allTokens = _.values(this.props.tokenByAddress); - const trackedTokens = _.filter(allTokens, t => t.isTracked); - const trackedTokensStartingWithEtherToken = trackedTokens.sort( - firstBy((t: Token) => t.symbol !== ETHER_TOKEN_SYMBOL) - .thenBy((t: Token) => t.symbol !== ZRX_TOKEN_SYMBOL) - .thenBy('address'), - ); - const tableRows = _.map( - trackedTokensStartingWithEtherToken, - this._renderTokenRow.bind(this, tokenColSpan, actionPaddingX), - ); - return tableRows; - } - private _renderTokenRow(tokenColSpan: number, actionPaddingX: number, token: Token) { - const tokenState = this.props.tokenStateByAddress[token.address]; - const tokenLink = utils.getEtherScanLinkIfExists( - token.address, - this.props.networkId, - EtherscanLinkSuffixes.Address, - ); - const isMintable = - _.includes(configs.SYMBOLS_OF_MINTABLE_TOKENS, token.symbol) && - this.props.networkId !== constants.NETWORK_ID_MAINNET; - return ( - - - {_.isUndefined(tokenLink) ? ( - this._renderTokenName(token) - ) : ( - - {this._renderTokenName(token)} - - )} - - - {this._renderAmount(tokenState.balance, token.decimals)} {token.symbol} - {this.state.isZRXSpinnerVisible && - token.symbol === ZRX_TOKEN_SYMBOL && ( - - - - )} - - - - - - {isMintable && ( - Minting...} - labelComplete="Minted!" - onClickAsyncFn={this._onMintTestTokensAsync.bind(this, token)} - /> - )} - {token.symbol === ZRX_TOKEN_SYMBOL && - this.props.networkId === constants.NETWORK_ID_TESTNET && ( - - )} - - {this.props.screenWidth !== ScreenWidths.Sm && ( - - - - )} - - ); - } - private _onAssetTokenPicked(tokenAddress: string) { - if (_.isEmpty(tokenAddress)) { - this.setState({ - isTokenPickerOpen: false, - }); - return; - } - const token = this.props.tokenByAddress[tokenAddress]; - const isDefaultTrackedToken = _.includes(configs.DEFAULT_TRACKED_TOKEN_SYMBOLS, token.symbol); - if (!this.state.isAddingToken && !isDefaultTrackedToken) { - if (token.isRegistered) { - // Remove the token from tracked tokens - const newToken = { - ...token, - isTracked: false, - }; - this.props.dispatcher.updateTokenByAddress([newToken]); - } else { - this.props.dispatcher.removeTokenToTokenByAddress(token); - } - this.props.dispatcher.removeFromTokenStateByAddress(tokenAddress); - trackedTokenStorage.removeTrackedToken(this.props.userAddress, this.props.networkId, tokenAddress); - } else if (isDefaultTrackedToken) { - this.props.dispatcher.showFlashMessage(`Cannot remove ${token.name} because it's a default token`); - } - this.setState({ - isTokenPickerOpen: false, - }); - } - private _onSendFailed() { - this.setState({ - errorType: BalanceErrs.sendFailed, - }); - } - private _renderAmount(amount: BigNumber, decimals: number) { - const unitAmount = ZeroEx.toUnitAmount(amount, decimals); - return unitAmount.toNumber().toFixed(PRECISION); - } - private _renderTokenName(token: Token) { - const tooltipId = `tooltip-${token.address}`; - return ( -
- -
- {token.name} -
- {token.address} -
- ); - } - private _renderErrorDialogBody() { - switch (this.state.errorType) { - case BalanceErrs.incorrectNetworkForFaucet: - return ( -
- Our faucet can only send test Ether to addresses on the {constants.TESTNET_NAME} testnet - (networkId {constants.NETWORK_ID_TESTNET}). Please make sure you are connected to the{' '} - {constants.TESTNET_NAME} testnet and try requesting ether again. -
- ); +
+ + + + Currency + Balance + + {isTestNetwork && ( + + {isSmallScreen ? 'Faucet' : 'Request from faucet'} + + )} + {isTestNetwork && ( + + {isSmallScreen ? 'Loan' : 'Request Dharma loan'} + + + )} + + + + + + + + + {this.props.userEtherBalance.toFixed(PRECISION)} ETH + {this.state.isBalanceSpinnerVisible && ( + + + + )} + + + {isTestNetwork && ( + + + + )} + {isTestNetwork && ( + + + + )} + + +
+
+
+

{isTestNetwork ? 'Test tokens' : 'Tokens'}

+
+
+ + + +
+
+ + + +
+
+ +
+ {isTestNetwork + ? "Mint some test tokens you'd like to use to generate or fill an order using 0x." + : "Set trading permissions for a token you'd like to start trading."} +
+ + + + Token + Balance + +
Allowance
+ +
+ Action + {this.props.screenWidth !== ScreenWidths.Sm && Send} +
+
+ {this._renderTokenTableRows()} +
+ + {this._renderErrorDialogBody()} + + + {this._renderDharmaLoanFrame()} + + +
+ ); + } + private _renderTokenTableRows() { + if (!this.props.blockchainIsLoaded || this.props.blockchainErr !== BlockchainErrs.NoError) { + return ''; + } + const isSmallScreen = this.props.screenWidth === ScreenWidths.Sm; + const tokenColSpan = isSmallScreen ? TOKEN_COL_SPAN_SM : TOKEN_COL_SPAN_LG; + const actionPaddingX = isSmallScreen ? 2 : 24; + const allTokens = _.values(this.props.tokenByAddress); + const trackedTokens = _.filter(allTokens, t => t.isTracked); + const trackedTokensStartingWithEtherToken = trackedTokens.sort( + firstBy((t: Token) => t.symbol !== ETHER_TOKEN_SYMBOL) + .thenBy((t: Token) => t.symbol !== ZRX_TOKEN_SYMBOL) + .thenBy('address'), + ); + const tableRows = _.map( + trackedTokensStartingWithEtherToken, + this._renderTokenRow.bind(this, tokenColSpan, actionPaddingX), + ); + return tableRows; + } + private _renderTokenRow(tokenColSpan: number, actionPaddingX: number, token: Token) { + const tokenState = this.props.tokenStateByAddress[token.address]; + const tokenLink = utils.getEtherScanLinkIfExists( + token.address, + this.props.networkId, + EtherscanLinkSuffixes.Address, + ); + const isMintable = + _.includes(configs.SYMBOLS_OF_MINTABLE_TOKENS, token.symbol) && + this.props.networkId !== constants.NETWORK_ID_MAINNET; + return ( + + + {_.isUndefined(tokenLink) ? ( + this._renderTokenName(token) + ) : ( + + {this._renderTokenName(token)} + + )} + + + {this._renderAmount(tokenState.balance, token.decimals)} {token.symbol} + {this.state.isZRXSpinnerVisible && + token.symbol === ZRX_TOKEN_SYMBOL && ( + + + + )} + + + + + + {isMintable && ( + Minting...} + labelComplete="Minted!" + onClickAsyncFn={this._onMintTestTokensAsync.bind(this, token)} + /> + )} + {token.symbol === ZRX_TOKEN_SYMBOL && + this.props.networkId === constants.NETWORK_ID_TESTNET && ( + + )} + + {this.props.screenWidth !== ScreenWidths.Sm && ( + + + + )} + + ); + } + private _onAssetTokenPicked(tokenAddress: string) { + if (_.isEmpty(tokenAddress)) { + this.setState({ + isTokenPickerOpen: false, + }); + return; + } + const token = this.props.tokenByAddress[tokenAddress]; + const isDefaultTrackedToken = _.includes(configs.DEFAULT_TRACKED_TOKEN_SYMBOLS, token.symbol); + if (!this.state.isAddingToken && !isDefaultTrackedToken) { + if (token.isRegistered) { + // Remove the token from tracked tokens + const newToken = { + ...token, + isTracked: false, + }; + this.props.dispatcher.updateTokenByAddress([newToken]); + } else { + this.props.dispatcher.removeTokenToTokenByAddress(token); + } + this.props.dispatcher.removeFromTokenStateByAddress(tokenAddress); + trackedTokenStorage.removeTrackedToken(this.props.userAddress, this.props.networkId, tokenAddress); + } else if (isDefaultTrackedToken) { + this.props.dispatcher.showFlashMessage(`Cannot remove ${token.name} because it's a default token`); + } + this.setState({ + isTokenPickerOpen: false, + }); + } + private _onSendFailed() { + this.setState({ + errorType: BalanceErrs.sendFailed, + }); + } + private _renderAmount(amount: BigNumber, decimals: number) { + const unitAmount = ZeroEx.toUnitAmount(amount, decimals); + return unitAmount.toNumber().toFixed(PRECISION); + } + private _renderTokenName(token: Token) { + const tooltipId = `tooltip-${token.address}`; + return ( +
+ +
+ {token.name} +
+ {token.address} +
+ ); + } + private _renderErrorDialogBody() { + switch (this.state.errorType) { + case BalanceErrs.incorrectNetworkForFaucet: + return ( +
+ Our faucet can only send test Ether to addresses on the {constants.TESTNET_NAME} testnet + (networkId {constants.NETWORK_ID_TESTNET}). Please make sure you are connected to the{' '} + {constants.TESTNET_NAME} testnet and try requesting ether again. +
+ ); - case BalanceErrs.faucetRequestFailed: - return ( -
- An unexpected error occurred while trying to request test Ether from our faucet. Please refresh - the page and try again. -
- ); + case BalanceErrs.faucetRequestFailed: + return ( +
+ An unexpected error occurred while trying to request test Ether from our faucet. Please refresh + the page and try again. +
+ ); - case BalanceErrs.faucetQueueIsFull: - return
Our test Ether faucet queue is full. Please try requesting test Ether again later.
; + case BalanceErrs.faucetQueueIsFull: + return
Our test Ether faucet queue is full. Please try requesting test Ether again later.
; - case BalanceErrs.mintingFailed: - return
Minting your test tokens failed unexpectedly. Please refresh the page and try again.
; + case BalanceErrs.mintingFailed: + return
Minting your test tokens failed unexpectedly. Please refresh the page and try again.
; - case BalanceErrs.allowanceSettingFailed: - return ( -
- An unexpected error occurred while trying to set your test token allowance. Please refresh the - page and try again. -
- ); + case BalanceErrs.allowanceSettingFailed: + return ( +
+ An unexpected error occurred while trying to set your test token allowance. Please refresh the + page and try again. +
+ ); - case undefined: - return null; // No error to show + case undefined: + return null; // No error to show - default: - throw utils.spawnSwitchErr('errorType', this.state.errorType); - } - } - private _renderDharmaLoanFrame() { - if (utils.isUserOnMobile()) { - return ( -

- We apologize -- Dharma loan requests are not available on mobile yet. Please try again through your - desktop browser. -

- ); - } else { - return ( - - ); - } - } - private _onErrorOccurred(errorType: BalanceErrs) { - this.setState({ - errorType, - }); - } - private async _onMintTestTokensAsync(token: Token): Promise { - try { - await this.props.blockchain.mintTestTokensAsync(token); - const amount = ZeroEx.toUnitAmount(constants.MINT_AMOUNT, token.decimals); - this.props.dispatcher.showFlashMessage(`Successfully minted ${amount.toString(10)} ${token.symbol}`); - return true; - } catch (err) { - const errMsg = `${err}`; - if (_.includes(errMsg, BlockchainCallErrs.UserHasNoAssociatedAddresses)) { - this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); - return false; - } - if (_.includes(errMsg, 'User denied transaction')) { - return false; - } - utils.consoleLog(`Unexpected error encountered: ${err}`); - utils.consoleLog(err.stack); - this.setState({ - errorType: BalanceErrs.mintingFailed, - }); - await errorReporter.reportAsync(err); - return false; - } - } - private async _faucetRequestAsync(isEtherRequest: boolean): Promise { - if (this.props.userAddress === '') { - this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); - return false; - } + default: + throw utils.spawnSwitchErr('errorType', this.state.errorType); + } + } + private _renderDharmaLoanFrame() { + if (utils.isUserOnMobile()) { + return ( +

+ We apologize -- Dharma loan requests are not available on mobile yet. Please try again through your + desktop browser. +

+ ); + } else { + return ( + + ); + } + } + private _onErrorOccurred(errorType: BalanceErrs) { + this.setState({ + errorType, + }); + } + private async _onMintTestTokensAsync(token: Token): Promise { + try { + await this.props.blockchain.mintTestTokensAsync(token); + const amount = ZeroEx.toUnitAmount(constants.MINT_AMOUNT, token.decimals); + this.props.dispatcher.showFlashMessage(`Successfully minted ${amount.toString(10)} ${token.symbol}`); + return true; + } catch (err) { + const errMsg = `${err}`; + if (_.includes(errMsg, BlockchainCallErrs.UserHasNoAssociatedAddresses)) { + this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); + return false; + } + if (_.includes(errMsg, 'User denied transaction')) { + return false; + } + utils.consoleLog(`Unexpected error encountered: ${err}`); + utils.consoleLog(err.stack); + this.setState({ + errorType: BalanceErrs.mintingFailed, + }); + await errorReporter.reportAsync(err); + return false; + } + } + private async _faucetRequestAsync(isEtherRequest: boolean): Promise { + if (this.props.userAddress === '') { + this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); + return false; + } - // If on another network other then the testnet our faucet serves test ether - // from, we must show user an error message - if (this.props.blockchain.networkId !== constants.NETWORK_ID_TESTNET) { - this.setState({ - errorType: BalanceErrs.incorrectNetworkForFaucet, - }); - return false; - } + // If on another network other then the testnet our faucet serves test ether + // from, we must show user an error message + if (this.props.blockchain.networkId !== constants.NETWORK_ID_TESTNET) { + this.setState({ + errorType: BalanceErrs.incorrectNetworkForFaucet, + }); + return false; + } - await utils.sleepAsync(ARTIFICIAL_FAUCET_REQUEST_DELAY); + await utils.sleepAsync(ARTIFICIAL_FAUCET_REQUEST_DELAY); - const segment = isEtherRequest ? 'ether' : 'zrx'; - const response = await fetch(`${constants.URL_ETHER_FAUCET}/${segment}/${this.props.userAddress}`); - const responseBody = await response.text(); - if (response.status !== constants.SUCCESS_STATUS) { - utils.consoleLog(`Unexpected status code: ${response.status} -> ${responseBody}`); - const errorType = - response.status === constants.UNAVAILABLE_STATUS - ? BalanceErrs.faucetQueueIsFull - : BalanceErrs.faucetRequestFailed; - this.setState({ - errorType, - }); - await errorReporter.reportAsync(new Error(`Faucet returned non-200: ${JSON.stringify(response)}`)); - return false; - } + const segment = isEtherRequest ? 'ether' : 'zrx'; + const response = await fetch(`${constants.URL_ETHER_FAUCET}/${segment}/${this.props.userAddress}`); + const responseBody = await response.text(); + if (response.status !== constants.SUCCESS_STATUS) { + utils.consoleLog(`Unexpected status code: ${response.status} -> ${responseBody}`); + const errorType = + response.status === constants.UNAVAILABLE_STATUS + ? BalanceErrs.faucetQueueIsFull + : BalanceErrs.faucetRequestFailed; + this.setState({ + errorType, + }); + await errorReporter.reportAsync(new Error(`Faucet returned non-200: ${JSON.stringify(response)}`)); + return false; + } - if (isEtherRequest) { - this.setState({ - isBalanceSpinnerVisible: true, - }); - } else { - const tokens = _.values(this.props.tokenByAddress); - const zrxToken = _.find(tokens, t => t.symbol === ZRX_TOKEN_SYMBOL); - const zrxTokenState = this.props.tokenStateByAddress[zrxToken.address]; - this.setState({ - isZRXSpinnerVisible: true, - currentZrxBalance: zrxTokenState.balance, - }); - // tslint:disable-next-line:no-floating-promises - this.props.blockchain.pollTokenBalanceAsync(zrxToken); - } - return true; - } - private _onErrorDialogToggle(isOpen: boolean) { - this.setState({ - errorType: undefined, - }); - } - private _onDharmaDialogToggle() { - this.setState({ - isDharmaDialogVisible: !this.state.isDharmaDialogVisible, - }); - } - private _onAddTokenClicked() { - this.setState({ - isTokenPickerOpen: true, - isAddingToken: true, - }); - } - private _onRemoveTokenClicked() { - this.setState({ - isTokenPickerOpen: true, - isAddingToken: false, - }); - } + if (isEtherRequest) { + this.setState({ + isBalanceSpinnerVisible: true, + }); + } else { + const tokens = _.values(this.props.tokenByAddress); + const zrxToken = _.find(tokens, t => t.symbol === ZRX_TOKEN_SYMBOL); + const zrxTokenState = this.props.tokenStateByAddress[zrxToken.address]; + this.setState({ + isZRXSpinnerVisible: true, + currentZrxBalance: zrxTokenState.balance, + }); + // tslint:disable-next-line:no-floating-promises + this.props.blockchain.pollTokenBalanceAsync(zrxToken); + } + return true; + } + private _onErrorDialogToggle(isOpen: boolean) { + this.setState({ + errorType: undefined, + }); + } + private _onDharmaDialogToggle() { + this.setState({ + isDharmaDialogVisible: !this.state.isDharmaDialogVisible, + }); + } + private _onAddTokenClicked() { + this.setState({ + isTokenPickerOpen: true, + isAddingToken: true, + }); + } + private _onRemoveTokenClicked() { + this.setState({ + isTokenPickerOpen: true, + isAddingToken: false, + }); + } } // tslint:disable:max-file-line-count diff --git a/packages/website/ts/components/top_bar.tsx b/packages/website/ts/components/top_bar.tsx index 1f111cb07..11d3e7cc2 100644 --- a/packages/website/ts/components/top_bar.tsx +++ b/packages/website/ts/components/top_bar.tsx @@ -15,333 +15,333 @@ import { colors } from 'ts/utils/colors'; import { constants } from 'ts/utils/constants'; interface TopBarProps { - userAddress?: string; - blockchainIsLoaded: boolean; - location: Location; - docsVersion?: string; - availableDocVersions?: string[]; - menu?: DocsMenu; - menuSubsectionsBySection?: MenuSubsectionsBySection; - shouldFullWidth?: boolean; - docsInfo?: DocsInfo; - style?: React.CSSProperties; - isNightVersion?: boolean; + userAddress?: string; + blockchainIsLoaded: boolean; + location: Location; + docsVersion?: string; + availableDocVersions?: string[]; + menu?: DocsMenu; + menuSubsectionsBySection?: MenuSubsectionsBySection; + shouldFullWidth?: boolean; + docsInfo?: DocsInfo; + style?: React.CSSProperties; + isNightVersion?: boolean; } interface TopBarState { - isDrawerOpen: boolean; + isDrawerOpen: boolean; } const styles: Styles = { - address: { - marginRight: 12, - overflow: 'hidden', - paddingTop: 4, - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - width: 70, - }, - topBar: { - backgroundcolor: colors.white, - height: 59, - width: '100%', - position: 'relative', - top: 0, - zIndex: 1100, - paddingBottom: 1, - }, - bottomBar: { - boxShadow: 'rgba(0, 0, 0, 0.187647) 0px 1px 3px', - }, - menuItem: { - fontSize: 14, - color: colors.darkestGrey, - paddingTop: 6, - paddingBottom: 6, - marginTop: 17, - cursor: 'pointer', - fontWeight: 400, - }, + address: { + marginRight: 12, + overflow: 'hidden', + paddingTop: 4, + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + width: 70, + }, + topBar: { + backgroundcolor: colors.white, + height: 59, + width: '100%', + position: 'relative', + top: 0, + zIndex: 1100, + paddingBottom: 1, + }, + bottomBar: { + boxShadow: 'rgba(0, 0, 0, 0.187647) 0px 1px 3px', + }, + menuItem: { + fontSize: 14, + color: colors.darkestGrey, + paddingTop: 6, + paddingBottom: 6, + marginTop: 17, + cursor: 'pointer', + fontWeight: 400, + }, }; export class TopBar extends React.Component { - public static defaultProps: Partial = { - shouldFullWidth: false, - style: {}, - isNightVersion: false, - }; - constructor(props: TopBarProps) { - super(props); - this.state = { - isDrawerOpen: false, - }; - } - public render() { - const isNightVersion = this.props.isNightVersion; - const isFullWidthPage = this.props.shouldFullWidth; - const parentClassNames = `flex mx-auto ${isFullWidthPage ? 'pl2' : 'max-width-4'}`; - const developerSectionMenuItems = [ - - - , - - - , - - - , - - - , - - - , - - - , - ]; - const bottomBorderStyle = this._shouldDisplayBottomBar() ? styles.bottomBar : {}; - const fullWidthClasses = isFullWidthPage ? 'pr4' : ''; - const logoUrl = isNightVersion ? '/images/protocol_logo_white.png' : '/images/protocol_logo_black.png'; - const menuClasses = `col col-${isFullWidthPage ? '4' : '5'} ${fullWidthClasses} lg-pr0 md-pr2 sm-hide xs-hide`; - const menuIconStyle = { - fontSize: 25, - color: isNightVersion ? 'white' : 'black', - cursor: 'pointer', - paddingTop: 16, - }; - return ( -
-
-
- - - -
-
-
- {!this._isViewingPortal() && ( -
-
- - - - -
-
- )} - {this.props.blockchainIsLoaded && - !_.isEmpty(this.props.userAddress) && ( -
{this._renderUser()}
- )} -
-
- -
-
-
- {this._renderDrawer()} -
- ); - } - private _renderDrawer() { - return ( - - {this._renderPortalMenu()} - {this._renderDocsMenu()} - {this._renderWiki()} -
- Website -
- - Home - - - Wiki - - {!this._isViewing0xjsDocs() && ( - - 0x.js Docs - - )} - {!this._isViewingConnectDocs() && ( - - 0x Connect Docs - - )} - {!this._isViewingSmartContractsDocs() && ( - - Smart Contract Docs - - )} - {!this._isViewingPortal() && ( - - Portal DApp - - )} - - Whitepaper - - - About - - - Blog - - - - FAQ - - -
- ); - } - private _renderDocsMenu(): React.ReactNode { - if ( - (!this._isViewing0xjsDocs() && !this._isViewingSmartContractsDocs() && !this._isViewingConnectDocs()) || - _.isUndefined(this.props.menu) - ) { - return undefined; - } + public static defaultProps: Partial = { + shouldFullWidth: false, + style: {}, + isNightVersion: false, + }; + constructor(props: TopBarProps) { + super(props); + this.state = { + isDrawerOpen: false, + }; + } + public render() { + const isNightVersion = this.props.isNightVersion; + const isFullWidthPage = this.props.shouldFullWidth; + const parentClassNames = `flex mx-auto ${isFullWidthPage ? 'pl2' : 'max-width-4'}`; + const developerSectionMenuItems = [ + + + , + + + , + + + , + + + , + + + , + + + , + ]; + const bottomBorderStyle = this._shouldDisplayBottomBar() ? styles.bottomBar : {}; + const fullWidthClasses = isFullWidthPage ? 'pr4' : ''; + const logoUrl = isNightVersion ? '/images/protocol_logo_white.png' : '/images/protocol_logo_black.png'; + const menuClasses = `col col-${isFullWidthPage ? '4' : '5'} ${fullWidthClasses} lg-pr0 md-pr2 sm-hide xs-hide`; + const menuIconStyle = { + fontSize: 25, + color: isNightVersion ? 'white' : 'black', + cursor: 'pointer', + paddingTop: 16, + }; + return ( +
+
+
+ + + +
+
+
+ {!this._isViewingPortal() && ( +
+
+ + + + +
+
+ )} + {this.props.blockchainIsLoaded && + !_.isEmpty(this.props.userAddress) && ( +
{this._renderUser()}
+ )} +
+
+ +
+
+
+ {this._renderDrawer()} +
+ ); + } + private _renderDrawer() { + return ( + + {this._renderPortalMenu()} + {this._renderDocsMenu()} + {this._renderWiki()} +
+ Website +
+ + Home + + + Wiki + + {!this._isViewing0xjsDocs() && ( + + 0x.js Docs + + )} + {!this._isViewingConnectDocs() && ( + + 0x Connect Docs + + )} + {!this._isViewingSmartContractsDocs() && ( + + Smart Contract Docs + + )} + {!this._isViewingPortal() && ( + + Portal DApp + + )} + + Whitepaper + + + About + + + Blog + + + + FAQ + + +
+ ); + } + private _renderDocsMenu(): React.ReactNode { + if ( + (!this._isViewing0xjsDocs() && !this._isViewingSmartContractsDocs() && !this._isViewingConnectDocs()) || + _.isUndefined(this.props.menu) + ) { + return undefined; + } - const sectionTitle = `${this.props.docsInfo.displayName} Docs`; - return ( -
-
- {sectionTitle} -
- -
- ); - } - private _renderWiki(): React.ReactNode { - if (!this._isViewingWiki()) { - return undefined; - } + const sectionTitle = `${this.props.docsInfo.displayName} Docs`; + return ( +
+
+ {sectionTitle} +
+ +
+ ); + } + private _renderWiki(): React.ReactNode { + if (!this._isViewingWiki()) { + return undefined; + } - return ( -
-
- 0x Protocol Wiki -
- -
- ); - } - private _renderPortalMenu(): React.ReactNode { - if (!this._isViewingPortal()) { - return undefined; - } + return ( +
+
+ 0x Protocol Wiki +
+ +
+ ); + } + private _renderPortalMenu(): React.ReactNode { + if (!this._isViewingPortal()) { + return undefined; + } - return ( -
-
- Portal DApp -
- -
- ); - } - private _renderUser() { - const userAddress = this.props.userAddress; - const identiconDiameter = 26; - return ( -
-
- {!_.isEmpty(userAddress) ? userAddress : ''} -
- {userAddress} -
- -
-
- ); - } - private _onMenuButtonClick() { - this.setState({ - isDrawerOpen: !this.state.isDrawerOpen, - }); - } - private _isViewingPortal() { - return _.includes(this.props.location.pathname, WebsitePaths.Portal); - } - private _isViewingFAQ() { - return _.includes(this.props.location.pathname, WebsitePaths.FAQ); - } - private _isViewing0xjsDocs() { - return _.includes(this.props.location.pathname, WebsitePaths.ZeroExJs); - } - private _isViewingConnectDocs() { - return _.includes(this.props.location.pathname, WebsitePaths.Connect); - } - private _isViewingSmartContractsDocs() { - return _.includes(this.props.location.pathname, WebsitePaths.SmartContracts); - } - private _isViewingWiki() { - return _.includes(this.props.location.pathname, WebsitePaths.Wiki); - } - private _shouldDisplayBottomBar() { - return ( - this._isViewingWiki() || - this._isViewing0xjsDocs() || - this._isViewingFAQ() || - this._isViewingSmartContractsDocs() || - this._isViewingConnectDocs() - ); - } + return ( +
+
+ Portal DApp +
+ +
+ ); + } + private _renderUser() { + const userAddress = this.props.userAddress; + const identiconDiameter = 26; + return ( +
+
+ {!_.isEmpty(userAddress) ? userAddress : ''} +
+ {userAddress} +
+ +
+
+ ); + } + private _onMenuButtonClick() { + this.setState({ + isDrawerOpen: !this.state.isDrawerOpen, + }); + } + private _isViewingPortal() { + return _.includes(this.props.location.pathname, WebsitePaths.Portal); + } + private _isViewingFAQ() { + return _.includes(this.props.location.pathname, WebsitePaths.FAQ); + } + private _isViewing0xjsDocs() { + return _.includes(this.props.location.pathname, WebsitePaths.ZeroExJs); + } + private _isViewingConnectDocs() { + return _.includes(this.props.location.pathname, WebsitePaths.Connect); + } + private _isViewingSmartContractsDocs() { + return _.includes(this.props.location.pathname, WebsitePaths.SmartContracts); + } + private _isViewingWiki() { + return _.includes(this.props.location.pathname, WebsitePaths.Wiki); + } + private _shouldDisplayBottomBar() { + return ( + this._isViewingWiki() || + this._isViewing0xjsDocs() || + this._isViewingFAQ() || + this._isViewingSmartContractsDocs() || + this._isViewingConnectDocs() + ); + } } diff --git a/packages/website/ts/components/top_bar_menu_item.tsx b/packages/website/ts/components/top_bar_menu_item.tsx index 0138740ba..96ee86142 100644 --- a/packages/website/ts/components/top_bar_menu_item.tsx +++ b/packages/website/ts/components/top_bar_menu_item.tsx @@ -4,49 +4,49 @@ import { Link } from 'react-router-dom'; import { colors } from 'ts/utils/colors'; const DEFAULT_STYLE = { - color: colors.darkestGrey, + color: colors.darkestGrey, }; interface TopBarMenuItemProps { - title: string; - path?: string; - isPrimary?: boolean; - style?: React.CSSProperties; - className?: string; - isNightVersion?: boolean; + title: string; + path?: string; + isPrimary?: boolean; + style?: React.CSSProperties; + className?: string; + isNightVersion?: boolean; } interface TopBarMenuItemState {} export class TopBarMenuItem extends React.Component { - public static defaultProps: Partial = { - isPrimary: false, - style: DEFAULT_STYLE, - className: '', - isNightVersion: false, - }; - public render() { - const primaryStyles = this.props.isPrimary - ? { - borderRadius: 4, - border: `1px solid ${this.props.isNightVersion ? colors.grey : colors.greyishPink}`, - marginTop: 15, - paddingLeft: 9, - paddingRight: 9, - width: 77, - } - : {}; - const menuItemColor = this.props.isNightVersion ? 'white' : this.props.style.color; - const linkColor = _.isUndefined(menuItemColor) ? colors.darkestGrey : menuItemColor; - return ( -
- - {this.props.title} - -
- ); - } + public static defaultProps: Partial = { + isPrimary: false, + style: DEFAULT_STYLE, + className: '', + isNightVersion: false, + }; + public render() { + const primaryStyles = this.props.isPrimary + ? { + borderRadius: 4, + border: `1px solid ${this.props.isNightVersion ? colors.grey : colors.greyishPink}`, + marginTop: 15, + paddingLeft: 9, + paddingRight: 9, + width: 77, + } + : {}; + const menuItemColor = this.props.isNightVersion ? 'white' : this.props.style.color; + const linkColor = _.isUndefined(menuItemColor) ? colors.darkestGrey : menuItemColor; + return ( +
+ + {this.props.title} + +
+ ); + } } diff --git a/packages/website/ts/components/track_token_confirmation.tsx b/packages/website/ts/components/track_token_confirmation.tsx index 1887bd11c..76971aefa 100644 --- a/packages/website/ts/components/track_token_confirmation.tsx +++ b/packages/website/ts/components/track_token_confirmation.tsx @@ -6,56 +6,56 @@ import { colors } from 'ts/utils/colors'; import { utils } from 'ts/utils/utils'; interface TrackTokenConfirmationProps { - tokens: Token[]; - tokenByAddress: TokenByAddress; - networkId: number; - isAddingTokenToTracked: boolean; + tokens: Token[]; + tokenByAddress: TokenByAddress; + networkId: number; + isAddingTokenToTracked: boolean; } interface TrackTokenConfirmationState {} export class TrackTokenConfirmation extends React.Component { - public render() { - const isMultipleTokens = this.props.tokens.length > 1; - const allTokens = _.values(this.props.tokenByAddress); - return ( -
- {this.props.isAddingTokenToTracked ? ( -
- - - - Adding token{isMultipleTokens && 's'}... -
- ) : ( -
-
You do not currently track the following token{isMultipleTokens && 's'}:
-
- {_.map(this.props.tokens, (token: Token) => ( -
- -
- ))} -
-
- Tracking a token adds it to the balances section of 0x Portal and allows you to - generate/fill orders involving the token - {isMultipleTokens && 's'}. Would you like to start tracking{' '} - {isMultipleTokens ? 'these' : 'this'} token? -
-
- )} -
- ); - } + public render() { + const isMultipleTokens = this.props.tokens.length > 1; + const allTokens = _.values(this.props.tokenByAddress); + return ( +
+ {this.props.isAddingTokenToTracked ? ( +
+ + + + Adding token{isMultipleTokens && 's'}... +
+ ) : ( +
+
You do not currently track the following token{isMultipleTokens && 's'}:
+
+ {_.map(this.props.tokens, (token: Token) => ( +
+ +
+ ))} +
+
+ Tracking a token adds it to the balances section of 0x Portal and allows you to + generate/fill orders involving the token + {isMultipleTokens && 's'}. Would you like to start tracking{' '} + {isMultipleTokens ? 'these' : 'this'} token? +
+
+ )} +
+ ); + } } diff --git a/packages/website/ts/components/trade_history/trade_history.tsx b/packages/website/ts/components/trade_history/trade_history.tsx index 3963135f7..635358627 100644 --- a/packages/website/ts/components/trade_history/trade_history.tsx +++ b/packages/website/ts/components/trade_history/trade_history.tsx @@ -10,106 +10,106 @@ import { utils } from 'ts/utils/utils'; const FILL_POLLING_INTERVAL = 1000; interface TradeHistoryProps { - tokenByAddress: TokenByAddress; - userAddress: string; - networkId: number; + tokenByAddress: TokenByAddress; + userAddress: string; + networkId: number; } interface TradeHistoryState { - sortedFills: Fill[]; + sortedFills: Fill[]; } export class TradeHistory extends React.Component { - private _fillPollingIntervalId: number; - public constructor(props: TradeHistoryProps) { - super(props); - const sortedFills = this._getSortedFills(); - this.state = { - sortedFills, - }; - } - public componentWillMount() { - this._startPollingForFills(); - } - public componentWillUnmount() { - this._stopPollingForFills(); - } - public componentDidMount() { - window.scrollTo(0, 0); - } - public render() { - return ( -
-

Trade history

- -
- {this._renderTrades()} -
-
- ); - } - private _renderTrades() { - const numNonCustomFills = this._numFillsWithoutCustomERC20Tokens(); - if (numNonCustomFills === 0) { - return this._renderEmptyNotice(); - } + private _fillPollingIntervalId: number; + public constructor(props: TradeHistoryProps) { + super(props); + const sortedFills = this._getSortedFills(); + this.state = { + sortedFills, + }; + } + public componentWillMount() { + this._startPollingForFills(); + } + public componentWillUnmount() { + this._stopPollingForFills(); + } + public componentDidMount() { + window.scrollTo(0, 0); + } + public render() { + return ( +
+

Trade history

+ +
+ {this._renderTrades()} +
+
+ ); + } + private _renderTrades() { + const numNonCustomFills = this._numFillsWithoutCustomERC20Tokens(); + if (numNonCustomFills === 0) { + return this._renderEmptyNotice(); + } - return _.map(this.state.sortedFills, (fill, index) => { - return ( - - ); - }); - } - private _renderEmptyNotice() { - return ( - - No filled orders yet. - - ); - } - private _numFillsWithoutCustomERC20Tokens() { - let numNonCustomFills = 0; - const tokens = _.values(this.props.tokenByAddress); - _.each(this.state.sortedFills, fill => { - const takerToken = _.find(tokens, token => { - return token.address === fill.takerToken; - }); - const makerToken = _.find(tokens, token => { - return token.address === fill.makerToken; - }); - // For now we don't show history items for orders using custom ERC20 - // tokens the client does not know how to display. - // TODO: Try to retrieve the name/symbol of an unknown token in order to display it - // Be sure to remove similar logic in trade_history_item.tsx - if (!_.isUndefined(takerToken) && !_.isUndefined(makerToken)) { - numNonCustomFills += 1; - } - }); - return numNonCustomFills; - } - private _startPollingForFills() { - this._fillPollingIntervalId = window.setInterval(() => { - const sortedFills = this._getSortedFills(); - if (!utils.deepEqual(sortedFills, this.state.sortedFills)) { - this.setState({ - sortedFills, - }); - } - }, FILL_POLLING_INTERVAL); - } - private _stopPollingForFills() { - clearInterval(this._fillPollingIntervalId); - } - private _getSortedFills() { - const fillsByHash = tradeHistoryStorage.getUserFillsByHash(this.props.userAddress, this.props.networkId); - const fills = _.values(fillsByHash); - const sortedFills = _.sortBy(fills, [(fill: Fill) => fill.blockTimestamp * -1]); - return sortedFills; - } + return _.map(this.state.sortedFills, (fill, index) => { + return ( + + ); + }); + } + private _renderEmptyNotice() { + return ( + + No filled orders yet. + + ); + } + private _numFillsWithoutCustomERC20Tokens() { + let numNonCustomFills = 0; + const tokens = _.values(this.props.tokenByAddress); + _.each(this.state.sortedFills, fill => { + const takerToken = _.find(tokens, token => { + return token.address === fill.takerToken; + }); + const makerToken = _.find(tokens, token => { + return token.address === fill.makerToken; + }); + // For now we don't show history items for orders using custom ERC20 + // tokens the client does not know how to display. + // TODO: Try to retrieve the name/symbol of an unknown token in order to display it + // Be sure to remove similar logic in trade_history_item.tsx + if (!_.isUndefined(takerToken) && !_.isUndefined(makerToken)) { + numNonCustomFills += 1; + } + }); + return numNonCustomFills; + } + private _startPollingForFills() { + this._fillPollingIntervalId = window.setInterval(() => { + const sortedFills = this._getSortedFills(); + if (!utils.deepEqual(sortedFills, this.state.sortedFills)) { + this.setState({ + sortedFills, + }); + } + }, FILL_POLLING_INTERVAL); + } + private _stopPollingForFills() { + clearInterval(this._fillPollingIntervalId); + } + private _getSortedFills() { + const fillsByHash = tradeHistoryStorage.getUserFillsByHash(this.props.userAddress, this.props.networkId); + const fills = _.values(fillsByHash); + const sortedFills = _.sortBy(fills, [(fill: Fill) => fill.blockTimestamp * -1]); + return sortedFills; + } } diff --git a/packages/website/ts/components/trade_history/trade_history_item.tsx b/packages/website/ts/components/trade_history/trade_history_item.tsx index 118bde3b9..7e42e64e6 100644 --- a/packages/website/ts/components/trade_history/trade_history_item.tsx +++ b/packages/website/ts/components/trade_history/trade_history_item.tsx @@ -14,157 +14,157 @@ const PRECISION = 5; const IDENTICON_DIAMETER = 40; interface TradeHistoryItemProps { - fill: Fill; - tokenByAddress: TokenByAddress; - userAddress: string; - networkId: number; + fill: Fill; + tokenByAddress: TokenByAddress; + userAddress: string; + networkId: number; } interface TradeHistoryItemState {} export class TradeHistoryItem extends React.Component { - public render() { - const fill = this.props.fill; - const tokens = _.values(this.props.tokenByAddress); - const takerToken = _.find(tokens, token => { - return token.address === fill.takerToken; - }); - const makerToken = _.find(tokens, token => { - return token.address === fill.makerToken; - }); - // For now we don't show history items for orders using custom ERC20 - // tokens the client does not know how to display. - // TODO: Try to retrieve the name/symbol of an unknown token in order to display it - // Be sure to remove similar logic in trade_history.tsx - if (_.isUndefined(takerToken) || _.isUndefined(makerToken)) { - return null; - } + public render() { + const fill = this.props.fill; + const tokens = _.values(this.props.tokenByAddress); + const takerToken = _.find(tokens, token => { + return token.address === fill.takerToken; + }); + const makerToken = _.find(tokens, token => { + return token.address === fill.makerToken; + }); + // For now we don't show history items for orders using custom ERC20 + // tokens the client does not know how to display. + // TODO: Try to retrieve the name/symbol of an unknown token in order to display it + // Be sure to remove similar logic in trade_history.tsx + if (_.isUndefined(takerToken) || _.isUndefined(makerToken)) { + return null; + } - const amountColStyle: React.CSSProperties = { - fontWeight: 100, - display: 'inline-block', - }; - const amountColClassNames = - 'col col-12 lg-col-4 md-col-4 lg-py2 md-py2 sm-py1 lg-pr2 md-pr2 \ + const amountColStyle: React.CSSProperties = { + fontWeight: 100, + display: 'inline-block', + }; + const amountColClassNames = + 'col col-12 lg-col-4 md-col-4 lg-py2 md-py2 sm-py1 lg-pr2 md-pr2 \ lg-right-align md-right-align sm-center'; - return ( - -
-
{this._renderDate()}
-
-
- - - -
-
-
- {this._renderAmounts(makerToken, takerToken)} -
-
-
- -
-
-
-
- ); - } - private _renderAmounts(makerToken: Token, takerToken: Token) { - const fill = this.props.fill; - const filledTakerTokenAmountInUnits = ZeroEx.toUnitAmount(fill.filledTakerTokenAmount, takerToken.decimals); - const filledMakerTokenAmountInUnits = ZeroEx.toUnitAmount(fill.filledMakerTokenAmount, takerToken.decimals); - let exchangeRate = filledTakerTokenAmountInUnits.div(filledMakerTokenAmountInUnits); - const fillMakerTokenAmount = ZeroEx.toBaseUnitAmount(filledMakerTokenAmountInUnits, makerToken.decimals); + return ( + +
+
{this._renderDate()}
+
+
+ + + +
+
+
+ {this._renderAmounts(makerToken, takerToken)} +
+
+
+ +
+
+
+
+ ); + } + private _renderAmounts(makerToken: Token, takerToken: Token) { + const fill = this.props.fill; + const filledTakerTokenAmountInUnits = ZeroEx.toUnitAmount(fill.filledTakerTokenAmount, takerToken.decimals); + const filledMakerTokenAmountInUnits = ZeroEx.toUnitAmount(fill.filledMakerTokenAmount, takerToken.decimals); + let exchangeRate = filledTakerTokenAmountInUnits.div(filledMakerTokenAmountInUnits); + const fillMakerTokenAmount = ZeroEx.toBaseUnitAmount(filledMakerTokenAmountInUnits, makerToken.decimals); - let receiveAmount; - let receiveToken; - let givenAmount; - let givenToken; - if (this.props.userAddress === fill.maker && this.props.userAddress === fill.taker) { - receiveAmount = new BigNumber(0); - givenAmount = new BigNumber(0); - receiveToken = makerToken; - givenToken = takerToken; - } else if (this.props.userAddress === fill.maker) { - receiveAmount = fill.filledTakerTokenAmount; - givenAmount = fillMakerTokenAmount; - receiveToken = takerToken; - givenToken = makerToken; - exchangeRate = new BigNumber(1).div(exchangeRate); - } else if (this.props.userAddress === fill.taker) { - receiveAmount = fillMakerTokenAmount; - givenAmount = fill.filledTakerTokenAmount; - receiveToken = makerToken; - givenToken = takerToken; - } else { - // This condition should never be hit - throw new Error("Found Fill that wasn't performed by this user"); - } + let receiveAmount; + let receiveToken; + let givenAmount; + let givenToken; + if (this.props.userAddress === fill.maker && this.props.userAddress === fill.taker) { + receiveAmount = new BigNumber(0); + givenAmount = new BigNumber(0); + receiveToken = makerToken; + givenToken = takerToken; + } else if (this.props.userAddress === fill.maker) { + receiveAmount = fill.filledTakerTokenAmount; + givenAmount = fillMakerTokenAmount; + receiveToken = takerToken; + givenToken = makerToken; + exchangeRate = new BigNumber(1).div(exchangeRate); + } else if (this.props.userAddress === fill.taker) { + receiveAmount = fillMakerTokenAmount; + givenAmount = fill.filledTakerTokenAmount; + receiveToken = makerToken; + givenToken = takerToken; + } else { + // This condition should never be hit + throw new Error("Found Fill that wasn't performed by this user"); + } - return ( -
-
- + - {this._renderAmount(receiveAmount, receiveToken.symbol, receiveToken.decimals)} -
-
- - - {this._renderAmount(givenAmount, givenToken.symbol, givenToken.decimals)} -
-
- {exchangeRate.toFixed(PRECISION)} {givenToken.symbol}/{receiveToken.symbol} -
-
- ); - } - private _renderDate() { - const blockMoment = moment.unix(this.props.fill.blockTimestamp); - if (!blockMoment.isValid()) { - return null; - } + return ( +
+
+ + + {this._renderAmount(receiveAmount, receiveToken.symbol, receiveToken.decimals)} +
+
+ - + {this._renderAmount(givenAmount, givenToken.symbol, givenToken.decimals)} +
+
+ {exchangeRate.toFixed(PRECISION)} {givenToken.symbol}/{receiveToken.symbol} +
+
+ ); + } + private _renderDate() { + const blockMoment = moment.unix(this.props.fill.blockTimestamp); + if (!blockMoment.isValid()) { + return null; + } - const dayOfMonth = blockMoment.format('D'); - const monthAbreviation = blockMoment.format('MMM'); - const formattedBlockDate = blockMoment.format('H:mmA - MMMM D, YYYY'); - const dateTooltipId = `${this.props.fill.transactionHash}-date`; + const dayOfMonth = blockMoment.format('D'); + const monthAbreviation = blockMoment.format('MMM'); + const formattedBlockDate = blockMoment.format('H:mmA - MMMM D, YYYY'); + const dateTooltipId = `${this.props.fill.transactionHash}-date`; - return ( -
-
- {monthAbreviation} -
-
- {dayOfMonth} -
- {formattedBlockDate} -
- ); - } - private _renderAmount(amount: BigNumber, symbol: string, decimals: number) { - const unitAmount = ZeroEx.toUnitAmount(amount, decimals); - return ( - - {unitAmount.toFixed(PRECISION)} {symbol} - - ); - } + return ( +
+
+ {monthAbreviation} +
+
+ {dayOfMonth} +
+ {formattedBlockDate} +
+ ); + } + private _renderAmount(amount: BigNumber, symbol: string, decimals: number) { + const unitAmount = ZeroEx.toUnitAmount(amount, decimals); + return ( + + {unitAmount.toFixed(PRECISION)} {symbol} + + ); + } } diff --git a/packages/website/ts/components/ui/alert.tsx b/packages/website/ts/components/ui/alert.tsx index 91ef8f76e..54881b499 100644 --- a/packages/website/ts/components/ui/alert.tsx +++ b/packages/website/ts/components/ui/alert.tsx @@ -3,23 +3,23 @@ import { AlertTypes } from 'ts/types'; import { colors } from 'ts/utils/colors'; interface AlertProps { - type: AlertTypes; - message: string | React.ReactNode; + type: AlertTypes; + message: string | React.ReactNode; } export function Alert(props: AlertProps) { - const isAlert = props.type === AlertTypes.ERROR; - const errMsgStyles = { - background: isAlert ? colors.red200 : colors.lightestGreen, - color: colors.white, - marginTop: 10, - padding: 4, - paddingLeft: 8, - }; + const isAlert = props.type === AlertTypes.ERROR; + const errMsgStyles = { + background: isAlert ? colors.red200 : colors.lightestGreen, + color: colors.white, + marginTop: 10, + padding: 4, + paddingLeft: 8, + }; - return ( -
- {props.message} -
- ); + return ( +
+ {props.message} +
+ ); } diff --git a/packages/website/ts/components/ui/badge.tsx b/packages/website/ts/components/ui/badge.tsx index 1d2d81af6..7f7ea006e 100644 --- a/packages/website/ts/components/ui/badge.tsx +++ b/packages/website/ts/components/ui/badge.tsx @@ -3,55 +3,55 @@ import * as React from 'react'; import { Styles } from 'ts/types'; const styles: Styles = { - badge: { - width: 50, - fontSize: 11, - height: 10, - borderRadius: 5, - marginTop: 25, - lineHeight: 0.9, - fontFamily: 'Roboto Mono', - marginLeft: 3, - marginRight: 3, - }, + badge: { + width: 50, + fontSize: 11, + height: 10, + borderRadius: 5, + marginTop: 25, + lineHeight: 0.9, + fontFamily: 'Roboto Mono', + marginLeft: 3, + marginRight: 3, + }, }; interface BadgeProps { - title: string; - backgroundColor: string; + title: string; + backgroundColor: string; } interface BadgeState { - isHovering: boolean; + isHovering: boolean; } export class Badge extends React.Component { - constructor(props: BadgeProps) { - super(props); - this.state = { - isHovering: false, - }; - } - public render() { - const badgeStyle = { - ...styles.badge, - backgroundColor: this.props.backgroundColor, - opacity: this.state.isHovering ? 0.7 : 1, - }; - return ( -
- {this.props.title} -
- ); - } - private _setHoverState(isHovering: boolean) { - this.setState({ - isHovering, - }); - } + constructor(props: BadgeProps) { + super(props); + this.state = { + isHovering: false, + }; + } + public render() { + const badgeStyle = { + ...styles.badge, + backgroundColor: this.props.backgroundColor, + opacity: this.state.isHovering ? 0.7 : 1, + }; + return ( +
+ {this.props.title} +
+ ); + } + private _setHoverState(isHovering: boolean) { + this.setState({ + isHovering, + }); + } } diff --git a/packages/website/ts/components/ui/copy_icon.tsx b/packages/website/ts/components/ui/copy_icon.tsx index 72ab77e1f..df55e0922 100644 --- a/packages/website/ts/components/ui/copy_icon.tsx +++ b/packages/website/ts/components/ui/copy_icon.tsx @@ -6,74 +6,74 @@ import ReactTooltip = require('react-tooltip'); import { colors } from 'ts/utils/colors'; interface CopyIconProps { - data: string; - callToAction?: string; + data: string; + callToAction?: string; } interface CopyIconState { - isHovering: boolean; + isHovering: boolean; } export class CopyIcon extends React.Component { - private _copyTooltipTimeoutId: number; - private _copyable: HTMLInputElement; - constructor(props: CopyIconProps) { - super(props); - this.state = { - isHovering: false, - }; - } - public componentDidUpdate() { - // Remove tooltip if hover away - if (!this.state.isHovering && this._copyTooltipTimeoutId) { - clearInterval(this._copyTooltipTimeoutId); - this._hideTooltip(); - } - } - public render() { - return ( -
- -
-
- -
- {this.props.callToAction &&
{this.props.callToAction}
} -
-
- Copied! -
- ); - } - private _setRefToProperty(el: HTMLInputElement) { - this._copyable = el; - } - private _setHoverState(isHovering: boolean) { - this.setState({ - isHovering, - }); - } - private _onCopy() { - if (this._copyTooltipTimeoutId) { - clearInterval(this._copyTooltipTimeoutId); - } + private _copyTooltipTimeoutId: number; + private _copyable: HTMLInputElement; + constructor(props: CopyIconProps) { + super(props); + this.state = { + isHovering: false, + }; + } + public componentDidUpdate() { + // Remove tooltip if hover away + if (!this.state.isHovering && this._copyTooltipTimeoutId) { + clearInterval(this._copyTooltipTimeoutId); + this._hideTooltip(); + } + } + public render() { + return ( +
+ +
+
+ +
+ {this.props.callToAction &&
{this.props.callToAction}
} +
+
+ Copied! +
+ ); + } + private _setRefToProperty(el: HTMLInputElement) { + this._copyable = el; + } + private _setHoverState(isHovering: boolean) { + this.setState({ + isHovering, + }); + } + private _onCopy() { + if (this._copyTooltipTimeoutId) { + clearInterval(this._copyTooltipTimeoutId); + } - const tooltipLifespanMs = 1000; - this._copyTooltipTimeoutId = window.setTimeout(() => { - this._hideTooltip(); - }, tooltipLifespanMs); - } - private _hideTooltip() { - ReactTooltip.hide(ReactDOM.findDOMNode(this._copyable)); - } + const tooltipLifespanMs = 1000; + this._copyTooltipTimeoutId = window.setTimeout(() => { + this._hideTooltip(); + }, tooltipLifespanMs); + } + private _hideTooltip() { + ReactTooltip.hide(ReactDOM.findDOMNode(this._copyable)); + } } diff --git a/packages/website/ts/components/ui/drop_down_menu_item.tsx b/packages/website/ts/components/ui/drop_down_menu_item.tsx index 64f88f318..a578fb4f9 100644 --- a/packages/website/ts/components/ui/drop_down_menu_item.tsx +++ b/packages/website/ts/components/ui/drop_down_menu_item.tsx @@ -6,99 +6,99 @@ import { colors } from 'ts/utils/colors'; const CHECK_CLOSE_POPOVER_INTERVAL_MS = 300; const DEFAULT_STYLE = { - fontSize: 14, + fontSize: 14, }; interface DropDownMenuItemProps { - title: string; - subMenuItems: React.ReactNode[]; - style?: React.CSSProperties; - menuItemStyle?: React.CSSProperties; - isNightVersion?: boolean; + title: string; + subMenuItems: React.ReactNode[]; + style?: React.CSSProperties; + menuItemStyle?: React.CSSProperties; + isNightVersion?: boolean; } interface DropDownMenuItemState { - isDropDownOpen: boolean; - anchorEl?: HTMLInputElement; + isDropDownOpen: boolean; + anchorEl?: HTMLInputElement; } export class DropDownMenuItem extends React.Component { - public static defaultProps: Partial = { - style: DEFAULT_STYLE, - menuItemStyle: DEFAULT_STYLE, - isNightVersion: false, - }; - private _isHovering: boolean; - private _popoverCloseCheckIntervalId: number; - constructor(props: DropDownMenuItemProps) { - super(props); - this.state = { - isDropDownOpen: false, - }; - } - public componentDidMount() { - this._popoverCloseCheckIntervalId = window.setInterval(() => { - this._checkIfShouldClosePopover(); - }, CHECK_CLOSE_POPOVER_INTERVAL_MS); - } - public componentWillUnmount() { - window.clearInterval(this._popoverCloseCheckIntervalId); - } - public render() { - const colorStyle = this.props.isNightVersion ? 'white' : this.props.style.color; - return ( -
-
-
{this.props.title}
-
- -
-
- -
- {this.props.subMenuItems} -
-
-
- ); - } - private _onHover(event: React.FormEvent) { - this._isHovering = true; - this._checkIfShouldOpenPopover(event); - } - private _checkIfShouldOpenPopover(event: React.FormEvent) { - if (this.state.isDropDownOpen) { - return; // noop - } + public static defaultProps: Partial = { + style: DEFAULT_STYLE, + menuItemStyle: DEFAULT_STYLE, + isNightVersion: false, + }; + private _isHovering: boolean; + private _popoverCloseCheckIntervalId: number; + constructor(props: DropDownMenuItemProps) { + super(props); + this.state = { + isDropDownOpen: false, + }; + } + public componentDidMount() { + this._popoverCloseCheckIntervalId = window.setInterval(() => { + this._checkIfShouldClosePopover(); + }, CHECK_CLOSE_POPOVER_INTERVAL_MS); + } + public componentWillUnmount() { + window.clearInterval(this._popoverCloseCheckIntervalId); + } + public render() { + const colorStyle = this.props.isNightVersion ? 'white' : this.props.style.color; + return ( +
+
+
{this.props.title}
+
+ +
+
+ +
+ {this.props.subMenuItems} +
+
+
+ ); + } + private _onHover(event: React.FormEvent) { + this._isHovering = true; + this._checkIfShouldOpenPopover(event); + } + private _checkIfShouldOpenPopover(event: React.FormEvent) { + if (this.state.isDropDownOpen) { + return; // noop + } - this.setState({ - isDropDownOpen: true, - anchorEl: event.currentTarget, - }); - } - private _onHoverOff(event: React.FormEvent) { - this._isHovering = false; - } - private _checkIfShouldClosePopover() { - if (!this.state.isDropDownOpen || this._isHovering) { - return; // noop - } - this._closePopover(); - } - private _closePopover() { - this.setState({ - isDropDownOpen: false, - }); - } + this.setState({ + isDropDownOpen: true, + anchorEl: event.currentTarget, + }); + } + private _onHoverOff(event: React.FormEvent) { + this._isHovering = false; + } + private _checkIfShouldClosePopover() { + if (!this.state.isDropDownOpen || this._isHovering) { + return; // noop + } + this._closePopover(); + } + private _closePopover() { + this.setState({ + isDropDownOpen: false, + }); + } } diff --git a/packages/website/ts/components/ui/ethereum_address.tsx b/packages/website/ts/components/ui/ethereum_address.tsx index ba51135be..b75d97e39 100644 --- a/packages/website/ts/components/ui/ethereum_address.tsx +++ b/packages/website/ts/components/ui/ethereum_address.tsx @@ -5,26 +5,26 @@ import { EtherscanLinkSuffixes } from 'ts/types'; import { utils } from 'ts/utils/utils'; interface EthereumAddressProps { - address: string; - networkId: number; + address: string; + networkId: number; } export const EthereumAddress = (props: EthereumAddressProps) => { - const tooltipId = `${props.address}-ethereum-address`; - const truncatedAddress = utils.getAddressBeginAndEnd(props.address); - return ( -
-
- {truncatedAddress} -
-
- -
- {props.address} -
- ); + const tooltipId = `${props.address}-ethereum-address`; + const truncatedAddress = utils.getAddressBeginAndEnd(props.address); + return ( +
+
+ {truncatedAddress} +
+
+ +
+ {props.address} +
+ ); }; diff --git a/packages/website/ts/components/ui/etherscan_icon.tsx b/packages/website/ts/components/ui/etherscan_icon.tsx index 5b224c3e1..3b17bd0fa 100644 --- a/packages/website/ts/components/ui/etherscan_icon.tsx +++ b/packages/website/ts/components/ui/etherscan_icon.tsx @@ -6,36 +6,36 @@ import { colors } from 'ts/utils/colors'; import { utils } from 'ts/utils/utils'; interface EtherScanIconProps { - addressOrTxHash: string; - etherscanLinkSuffixes: EtherscanLinkSuffixes; - networkId: number; + addressOrTxHash: string; + etherscanLinkSuffixes: EtherscanLinkSuffixes; + networkId: number; } export const EtherScanIcon = (props: EtherScanIconProps) => { - const etherscanLinkIfExists = utils.getEtherScanLinkIfExists( - props.addressOrTxHash, - props.networkId, - EtherscanLinkSuffixes.Address, - ); - const transactionTooltipId = `${props.addressOrTxHash}-etherscan-icon-tooltip`; - return ( -
- {!_.isUndefined(etherscanLinkIfExists) ? ( - - {renderIcon()} - - ) : ( -
- {renderIcon()} - - Your network (id: {props.networkId}) is not supported by Etherscan - -
- )} -
- ); + const etherscanLinkIfExists = utils.getEtherScanLinkIfExists( + props.addressOrTxHash, + props.networkId, + EtherscanLinkSuffixes.Address, + ); + const transactionTooltipId = `${props.addressOrTxHash}-etherscan-icon-tooltip`; + return ( +
+ {!_.isUndefined(etherscanLinkIfExists) ? ( + + {renderIcon()} + + ) : ( +
+ {renderIcon()} + + Your network (id: {props.networkId}) is not supported by Etherscan + +
+ )} +
+ ); }; function renderIcon() { - return ; + return ; } diff --git a/packages/website/ts/components/ui/fake_text_field.tsx b/packages/website/ts/components/ui/fake_text_field.tsx index 6d321bb46..f3d9410f6 100644 --- a/packages/website/ts/components/ui/fake_text_field.tsx +++ b/packages/website/ts/components/ui/fake_text_field.tsx @@ -3,32 +3,32 @@ import { InputLabel } from 'ts/components/ui/input_label'; import { Styles } from 'ts/types'; const styles: Styles = { - hr: { - borderBottom: '1px solid rgb(224, 224, 224)', - borderLeft: 'none rgb(224, 224, 224)', - borderRight: 'none rgb(224, 224, 224)', - borderTop: 'none rgb(224, 224, 224)', - bottom: 6, - boxSizing: 'content-box', - margin: 0, - position: 'absolute', - width: '100%', - }, + hr: { + borderBottom: '1px solid rgb(224, 224, 224)', + borderLeft: 'none rgb(224, 224, 224)', + borderRight: 'none rgb(224, 224, 224)', + borderTop: 'none rgb(224, 224, 224)', + bottom: 6, + boxSizing: 'content-box', + margin: 0, + position: 'absolute', + width: '100%', + }, }; interface FakeTextFieldProps { - label?: React.ReactNode | string; - children?: any; + label?: React.ReactNode | string; + children?: any; } export function FakeTextField(props: FakeTextFieldProps) { - return ( -
- {props.label !== '' && } -
- {props.children} -
-
-
- ); + return ( +
+ {props.label !== '' && } +
+ {props.children} +
+
+
+ ); } diff --git a/packages/website/ts/components/ui/flash_message.tsx b/packages/website/ts/components/ui/flash_message.tsx index 57a66d21f..2cb1fc764 100644 --- a/packages/website/ts/components/ui/flash_message.tsx +++ b/packages/website/ts/components/ui/flash_message.tsx @@ -6,35 +6,35 @@ import { Dispatcher } from 'ts/redux/dispatcher'; const SHOW_DURATION_MS = 4000; interface FlashMessageProps { - dispatcher: Dispatcher; - flashMessage?: string | React.ReactNode; - showDurationMs?: number; - bodyStyle?: React.CSSProperties; + dispatcher: Dispatcher; + flashMessage?: string | React.ReactNode; + showDurationMs?: number; + bodyStyle?: React.CSSProperties; } interface FlashMessageState {} export class FlashMessage extends React.Component { - public static defaultProps: Partial = { - showDurationMs: SHOW_DURATION_MS, - bodyStyle: {}, - }; - public render() { - if (!_.isUndefined(this.props.flashMessage)) { - return ( - - ); - } else { - return null; - } - } - private _onClose() { - this.props.dispatcher.hideFlashMessage(); - } + public static defaultProps: Partial = { + showDurationMs: SHOW_DURATION_MS, + bodyStyle: {}, + }; + public render() { + if (!_.isUndefined(this.props.flashMessage)) { + return ( + + ); + } else { + return null; + } + } + private _onClose() { + this.props.dispatcher.hideFlashMessage(); + } } diff --git a/packages/website/ts/components/ui/help_tooltip.tsx b/packages/website/ts/components/ui/help_tooltip.tsx index c946a70cb..d827eebb9 100644 --- a/packages/website/ts/components/ui/help_tooltip.tsx +++ b/packages/website/ts/components/ui/help_tooltip.tsx @@ -2,21 +2,21 @@ import * as React from 'react'; import ReactTooltip = require('react-tooltip'); interface HelpTooltipProps { - style?: React.CSSProperties; - explanation: React.ReactNode; + style?: React.CSSProperties; + explanation: React.ReactNode; } export const HelpTooltip = (props: HelpTooltipProps) => { - return ( -
- - -
- ); + return ( +
+ + +
+ ); }; diff --git a/packages/website/ts/components/ui/identicon.tsx b/packages/website/ts/components/ui/identicon.tsx index bad7a657d..bad6c2a78 100644 --- a/packages/website/ts/components/ui/identicon.tsx +++ b/packages/website/ts/components/ui/identicon.tsx @@ -4,45 +4,45 @@ import * as React from 'react'; import { constants } from 'ts/utils/constants'; interface IdenticonProps { - address: string; - diameter: number; - style?: React.CSSProperties; + address: string; + diameter: number; + style?: React.CSSProperties; } interface IdenticonState {} export class Identicon extends React.Component { - public static defaultProps: Partial = { - style: {}, - }; - public render() { - let address = this.props.address; - if (_.isEmpty(address)) { - address = constants.NULL_ADDRESS; - } - const diameter = this.props.diameter; - const icon = blockies({ - seed: address.toLowerCase(), - }); - return ( -
- -
- ); - } + public static defaultProps: Partial = { + style: {}, + }; + public render() { + let address = this.props.address; + if (_.isEmpty(address)) { + address = constants.NULL_ADDRESS; + } + const diameter = this.props.diameter; + const icon = blockies({ + seed: address.toLowerCase(), + }); + return ( +
+ +
+ ); + } } diff --git a/packages/website/ts/components/ui/input_label.tsx b/packages/website/ts/components/ui/input_label.tsx index 1cbda6692..e2009ad20 100644 --- a/packages/website/ts/components/ui/input_label.tsx +++ b/packages/website/ts/components/ui/input_label.tsx @@ -2,24 +2,24 @@ import * as React from 'react'; import { colors } from 'ts/utils/colors'; export interface InputLabelProps { - text: string | Element | React.ReactNode; + text: string | Element | React.ReactNode; } const styles = { - label: { - color: colors.grey, - fontSize: 12, - pointerEvents: 'none', - textAlign: 'left', - transform: 'scale(0.75) translate(0px, -28px)', - transformOrigin: 'left top 0px', - transition: 'all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms', - userSelect: 'none', - width: 240, - zIndex: 1, - }, + label: { + color: colors.grey, + fontSize: 12, + pointerEvents: 'none', + textAlign: 'left', + transform: 'scale(0.75) translate(0px, -28px)', + transformOrigin: 'left top 0px', + transition: 'all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms', + userSelect: 'none', + width: 240, + zIndex: 1, + }, }; export const InputLabel = (props: InputLabelProps) => { - return ; + return ; }; diff --git a/packages/website/ts/components/ui/lifecycle_raised_button.tsx b/packages/website/ts/components/ui/lifecycle_raised_button.tsx index fd23912f1..8ff856a75 100644 --- a/packages/website/ts/components/ui/lifecycle_raised_button.tsx +++ b/packages/website/ts/components/ui/lifecycle_raised_button.tsx @@ -7,97 +7,97 @@ import { utils } from 'ts/utils/utils'; const COMPLETE_STATE_SHOW_LENGTH_MS = 2000; enum ButtonState { - READY, - LOADING, - COMPLETE, + READY, + LOADING, + COMPLETE, } interface LifeCycleRaisedButtonProps { - isHidden?: boolean; - isDisabled?: boolean; - isPrimary?: boolean; - labelReady: React.ReactNode | string; - labelLoading: React.ReactNode | string; - labelComplete: React.ReactNode | string; - onClickAsyncFn: () => Promise; - backgroundColor?: string; - labelColor?: string; + isHidden?: boolean; + isDisabled?: boolean; + isPrimary?: boolean; + labelReady: React.ReactNode | string; + labelLoading: React.ReactNode | string; + labelComplete: React.ReactNode | string; + onClickAsyncFn: () => Promise; + backgroundColor?: string; + labelColor?: string; } interface LifeCycleRaisedButtonState { - buttonState: ButtonState; + buttonState: ButtonState; } export class LifeCycleRaisedButton extends React.Component { - public static defaultProps: Partial = { - isDisabled: false, - backgroundColor: colors.white, - labelColor: colors.darkGrey, - }; - private _buttonTimeoutId: number; - private _didUnmount: boolean; - constructor(props: LifeCycleRaisedButtonProps) { - super(props); - this.state = { - buttonState: ButtonState.READY, - }; - } - public componentWillUnmount() { - clearTimeout(this._buttonTimeoutId); - this._didUnmount = true; - } - public render() { - if (this.props.isHidden) { - return ; - } + public static defaultProps: Partial = { + isDisabled: false, + backgroundColor: colors.white, + labelColor: colors.darkGrey, + }; + private _buttonTimeoutId: number; + private _didUnmount: boolean; + constructor(props: LifeCycleRaisedButtonProps) { + super(props); + this.state = { + buttonState: ButtonState.READY, + }; + } + public componentWillUnmount() { + clearTimeout(this._buttonTimeoutId); + this._didUnmount = true; + } + public render() { + if (this.props.isHidden) { + return ; + } - let label; - switch (this.state.buttonState) { - case ButtonState.READY: - label = this.props.labelReady; - break; - case ButtonState.LOADING: - label = this.props.labelLoading; - break; - case ButtonState.COMPLETE: - label = this.props.labelComplete; - break; - default: - throw utils.spawnSwitchErr('ButtonState', this.state.buttonState); - } - return ( - - ); - } - public async onClickAsync() { - this.setState({ - buttonState: ButtonState.LOADING, - }); - const didSucceed = await this.props.onClickAsyncFn(); - if (this._didUnmount) { - return; // noop since unmount called before async callback returned. - } - if (didSucceed) { - this.setState({ - buttonState: ButtonState.COMPLETE, - }); - this._buttonTimeoutId = window.setTimeout(() => { - this.setState({ - buttonState: ButtonState.READY, - }); - }, COMPLETE_STATE_SHOW_LENGTH_MS); - } else { - this.setState({ - buttonState: ButtonState.READY, - }); - } - } + let label; + switch (this.state.buttonState) { + case ButtonState.READY: + label = this.props.labelReady; + break; + case ButtonState.LOADING: + label = this.props.labelLoading; + break; + case ButtonState.COMPLETE: + label = this.props.labelComplete; + break; + default: + throw utils.spawnSwitchErr('ButtonState', this.state.buttonState); + } + return ( + + ); + } + public async onClickAsync() { + this.setState({ + buttonState: ButtonState.LOADING, + }); + const didSucceed = await this.props.onClickAsyncFn(); + if (this._didUnmount) { + return; // noop since unmount called before async callback returned. + } + if (didSucceed) { + this.setState({ + buttonState: ButtonState.COMPLETE, + }); + this._buttonTimeoutId = window.setTimeout(() => { + this.setState({ + buttonState: ButtonState.READY, + }); + }, COMPLETE_STATE_SHOW_LENGTH_MS); + } else { + this.setState({ + buttonState: ButtonState.READY, + }); + } + } } diff --git a/packages/website/ts/components/ui/loading.tsx b/packages/website/ts/components/ui/loading.tsx index e9bfe3316..aa319e9e9 100644 --- a/packages/website/ts/components/ui/loading.tsx +++ b/packages/website/ts/components/ui/loading.tsx @@ -10,30 +10,30 @@ interface LoadingProps {} interface LoadingState {} export class Loading extends React.Component { - public render() { - return ( -
- - {utils.isUserOnMobile() ? ( - - ) : ( -
- -
- )} -
- Connecting to the blockchain... -
-
-
- ); - } + public render() { + return ( +
+ + {utils.isUserOnMobile() ? ( + + ) : ( +
+ +
+ )} +
+ Connecting to the blockchain... +
+
+
+ ); + } } diff --git a/packages/website/ts/components/ui/menu_item.tsx b/packages/website/ts/components/ui/menu_item.tsx index 956b5eae8..3482f436c 100644 --- a/packages/website/ts/components/ui/menu_item.tsx +++ b/packages/website/ts/components/ui/menu_item.tsx @@ -3,49 +3,49 @@ import * as React from 'react'; import { Link } from 'react-router-dom'; interface MenuItemProps { - to: string; - style?: React.CSSProperties; - onClick?: () => void; - className?: string; + to: string; + style?: React.CSSProperties; + onClick?: () => void; + className?: string; } interface MenuItemState { - isHovering: boolean; + isHovering: boolean; } export class MenuItem extends React.Component { - public static defaultProps: Partial = { - onClick: _.noop, - className: '', - }; - public constructor(props: MenuItemProps) { - super(props); - this.state = { - isHovering: false, - }; - } - public render() { - const menuItemStyles = { - cursor: 'pointer', - opacity: this.state.isHovering ? 0.5 : 1, - }; - return ( - -
- {this.props.children} -
- - ); - } - private _onToggleHover(isHovering: boolean) { - this.setState({ - isHovering, - }); - } + public static defaultProps: Partial = { + onClick: _.noop, + className: '', + }; + public constructor(props: MenuItemProps) { + super(props); + this.state = { + isHovering: false, + }; + } + public render() { + const menuItemStyles = { + cursor: 'pointer', + opacity: this.state.isHovering ? 0.5 : 1, + }; + return ( + +
+ {this.props.children} +
+ + ); + } + private _onToggleHover(isHovering: boolean) { + this.setState({ + isHovering, + }); + } } diff --git a/packages/website/ts/components/ui/party.tsx b/packages/website/ts/components/ui/party.tsx index ef3c7b425..ca2577b61 100644 --- a/packages/website/ts/components/ui/party.tsx +++ b/packages/website/ts/components/ui/party.tsx @@ -11,129 +11,129 @@ const IMAGE_DIMENSION = 100; const IDENTICON_DIAMETER = 95; interface PartyProps { - label: string; - address: string; - networkId: number; - alternativeImage?: string; - identiconDiameter?: number; - identiconStyle?: React.CSSProperties; - isInTokenRegistry?: boolean; - hasUniqueNameAndSymbol?: boolean; + label: string; + address: string; + networkId: number; + alternativeImage?: string; + identiconDiameter?: number; + identiconStyle?: React.CSSProperties; + isInTokenRegistry?: boolean; + hasUniqueNameAndSymbol?: boolean; } interface PartyState {} export class Party extends React.Component { - public static defaultProps: Partial = { - identiconStyle: {}, - identiconDiameter: IDENTICON_DIAMETER, - }; - public render() { - const label = this.props.label; - const address = this.props.address; - const identiconDiameter = this.props.identiconDiameter; - const emptyIdenticonStyles = { - width: identiconDiameter, - height: identiconDiameter, - backgroundColor: 'lightgray', - marginTop: 13, - marginBottom: 10, - }; - const tokenImageStyle = { - width: IMAGE_DIMENSION, - height: IMAGE_DIMENSION, - }; - const etherscanLinkIfExists = utils.getEtherScanLinkIfExists( - this.props.address, - this.props.networkId, - EtherscanLinkSuffixes.Address, - ); - const isRegistered = this.props.isInTokenRegistry; - const registeredTooltipId = `${this.props.address}-${isRegistered}-registeredTooltip`; - const uniqueNameAndSymbolTooltipId = `${this.props.address}-${isRegistered}-uniqueTooltip`; - return ( -
-
{label}
- {_.isEmpty(address) ? ( -
- ) : ( - - {isRegistered && !_.isUndefined(this.props.alternativeImage) ? ( - - ) : ( -
- -
- )} -
- )} -
-
- -
- {!_.isUndefined(this.props.isInTokenRegistry) && ( -
-
- - - {' '} - {isRegistered ? 'Registered' : 'Unregistered'} token - - {isRegistered ? ( -
- This token address was found in the token registry
- smart contract and is therefore believed to be a
- legitimate token. -
- ) : ( -
- This token is not included in the token registry
- smart contract. We cannot guarantee the legitimacy
- of this token. Make sure to verify its address on Etherscan. -
- )} -
-
-
- )} - {!_.isUndefined(this.props.hasUniqueNameAndSymbol) && - !this.props.hasUniqueNameAndSymbol && ( -
-
- - - {' '} - Suspicious token - - This token shares it's name, symbol or both with
- a token in the 0x Token Registry but it has a different
- smart contract address. This is most likely a scam token! -
-
-
- )} -
-
- ); - } + public static defaultProps: Partial = { + identiconStyle: {}, + identiconDiameter: IDENTICON_DIAMETER, + }; + public render() { + const label = this.props.label; + const address = this.props.address; + const identiconDiameter = this.props.identiconDiameter; + const emptyIdenticonStyles = { + width: identiconDiameter, + height: identiconDiameter, + backgroundColor: 'lightgray', + marginTop: 13, + marginBottom: 10, + }; + const tokenImageStyle = { + width: IMAGE_DIMENSION, + height: IMAGE_DIMENSION, + }; + const etherscanLinkIfExists = utils.getEtherScanLinkIfExists( + this.props.address, + this.props.networkId, + EtherscanLinkSuffixes.Address, + ); + const isRegistered = this.props.isInTokenRegistry; + const registeredTooltipId = `${this.props.address}-${isRegistered}-registeredTooltip`; + const uniqueNameAndSymbolTooltipId = `${this.props.address}-${isRegistered}-uniqueTooltip`; + return ( +
+
{label}
+ {_.isEmpty(address) ? ( +
+ ) : ( + + {isRegistered && !_.isUndefined(this.props.alternativeImage) ? ( + + ) : ( +
+ +
+ )} +
+ )} +
+
+ +
+ {!_.isUndefined(this.props.isInTokenRegistry) && ( +
+
+ + + {' '} + {isRegistered ? 'Registered' : 'Unregistered'} token + + {isRegistered ? ( +
+ This token address was found in the token registry
+ smart contract and is therefore believed to be a
+ legitimate token. +
+ ) : ( +
+ This token is not included in the token registry
+ smart contract. We cannot guarantee the legitimacy
+ of this token. Make sure to verify its address on Etherscan. +
+ )} +
+
+
+ )} + {!_.isUndefined(this.props.hasUniqueNameAndSymbol) && + !this.props.hasUniqueNameAndSymbol && ( +
+
+ + + {' '} + Suspicious token + + This token shares it's name, symbol or both with
+ a token in the 0x Token Registry but it has a different
+ smart contract address. This is most likely a scam token! +
+
+
+ )} +
+
+ ); + } } diff --git a/packages/website/ts/components/ui/required_label.tsx b/packages/website/ts/components/ui/required_label.tsx index 638683427..a5e7a22ce 100644 --- a/packages/website/ts/components/ui/required_label.tsx +++ b/packages/website/ts/components/ui/required_label.tsx @@ -2,14 +2,14 @@ import * as React from 'react'; import { colors } from 'ts/utils/colors'; export interface RequiredLabelProps { - label: string | React.ReactNode; + label: string | React.ReactNode; } export const RequiredLabel = (props: RequiredLabelProps) => { - return ( - - {props.label} - * - - ); + return ( + + {props.label} + * + + ); }; diff --git a/packages/website/ts/components/ui/simple_loading.tsx b/packages/website/ts/components/ui/simple_loading.tsx index 9f1fd5a13..81744196d 100644 --- a/packages/website/ts/components/ui/simple_loading.tsx +++ b/packages/website/ts/components/ui/simple_loading.tsx @@ -2,16 +2,16 @@ import CircularProgress from 'material-ui/CircularProgress'; import * as React from 'react'; export interface SimpleLoadingProps { - message: string; + message: string; } export const SimpleLoading = (props: SimpleLoadingProps) => { - return ( -
-
- -
{props.message}
-
-
- ); + return ( +
+
+ +
{props.message}
+
+
+ ); }; diff --git a/packages/website/ts/components/ui/swap_icon.tsx b/packages/website/ts/components/ui/swap_icon.tsx index 99e3450de..c41592287 100644 --- a/packages/website/ts/components/ui/swap_icon.tsx +++ b/packages/website/ts/components/ui/swap_icon.tsx @@ -3,40 +3,40 @@ import * as React from 'react'; import { colors } from 'ts/utils/colors'; interface SwapIconProps { - swapTokensFn: () => void; + swapTokensFn: () => void; } interface SwapIconState { - isHovering: boolean; + isHovering: boolean; } export class SwapIcon extends React.Component { - public constructor(props: SwapIconProps) { - super(props); - this.state = { - isHovering: false, - }; - } - public render() { - const swapStyles = { - color: this.state.isHovering ? colors.amber600 : colors.amber800, - fontSize: 50, - }; - return ( -
- -
- ); - } - private _onToggleHover(isHovering: boolean) { - this.setState({ - isHovering, - }); - } + public constructor(props: SwapIconProps) { + super(props); + this.state = { + isHovering: false, + }; + } + public render() { + const swapStyles = { + color: this.state.isHovering ? colors.amber600 : colors.amber800, + fontSize: 50, + }; + return ( +
+ +
+ ); + } + private _onToggleHover(isHovering: boolean) { + this.setState({ + isHovering, + }); + } } diff --git a/packages/website/ts/components/ui/token_icon.tsx b/packages/website/ts/components/ui/token_icon.tsx index a729821ce..ff57a96de 100644 --- a/packages/website/ts/components/ui/token_icon.tsx +++ b/packages/website/ts/components/ui/token_icon.tsx @@ -4,24 +4,24 @@ import { Identicon } from 'ts/components/ui/identicon'; import { Token } from 'ts/types'; interface TokenIconProps { - token: Token; - diameter: number; + token: Token; + diameter: number; } interface TokenIconState {} export class TokenIcon extends React.Component { - public render() { - const token = this.props.token; - const diameter = this.props.diameter; - return ( -
- {token.isRegistered && !_.isUndefined(token.iconUrl) ? ( - - ) : ( - - )} -
- ); - } + public render() { + const token = this.props.token; + const diameter = this.props.diameter; + return ( +
+ {token.isRegistered && !_.isUndefined(token.iconUrl) ? ( + + ) : ( + + )} +
+ ); + } } diff --git a/packages/website/ts/components/visual_order.tsx b/packages/website/ts/components/visual_order.tsx index a9779ac62..092954086 100644 --- a/packages/website/ts/components/visual_order.tsx +++ b/packages/website/ts/components/visual_order.tsx @@ -8,69 +8,69 @@ import { utils } from 'ts/utils/utils'; const PRECISION = 5; interface VisualOrderProps { - orderTakerAddress: string; - orderMakerAddress: string; - makerAssetToken: AssetToken; - takerAssetToken: AssetToken; - makerToken: Token; - takerToken: Token; - networkId: number; - tokenByAddress: TokenByAddress; - isMakerTokenAddressInRegistry: boolean; - isTakerTokenAddressInRegistry: boolean; + orderTakerAddress: string; + orderMakerAddress: string; + makerAssetToken: AssetToken; + takerAssetToken: AssetToken; + makerToken: Token; + takerToken: Token; + networkId: number; + tokenByAddress: TokenByAddress; + isMakerTokenAddressInRegistry: boolean; + isTakerTokenAddressInRegistry: boolean; } interface VisualOrderState {} export class VisualOrder extends React.Component { - public render() { - const allTokens = _.values(this.props.tokenByAddress); - const makerImage = this.props.makerToken.iconUrl; - const takerImage = this.props.takerToken.iconUrl; - return ( -
-
-
- -
-
-
- {this._renderAmount(this.props.takerAssetToken, this.props.takerToken)} -
-
- -
-
- {this._renderAmount(this.props.makerAssetToken, this.props.makerToken)} -
-
-
- -
-
-
- ); - } - private _renderAmount(assetToken: AssetToken, token: Token) { - const unitAmount = ZeroEx.toUnitAmount(assetToken.amount, token.decimals); - return ( -
- {unitAmount.toNumber().toFixed(PRECISION)} {token.symbol} -
- ); - } + public render() { + const allTokens = _.values(this.props.tokenByAddress); + const makerImage = this.props.makerToken.iconUrl; + const takerImage = this.props.takerToken.iconUrl; + return ( +
+
+
+ +
+
+
+ {this._renderAmount(this.props.takerAssetToken, this.props.takerToken)} +
+
+ +
+
+ {this._renderAmount(this.props.makerAssetToken, this.props.makerToken)} +
+
+
+ +
+
+
+ ); + } + private _renderAmount(assetToken: AssetToken, token: Token) { + const unitAmount = ZeroEx.toUnitAmount(assetToken.amount, token.decimals); + return ( +
+ {unitAmount.toNumber().toFixed(PRECISION)} {token.symbol} +
+ ); + } } diff --git a/packages/website/ts/containers/connect_documentation.tsx b/packages/website/ts/containers/connect_documentation.tsx index 2f5326d57..3e02a7d05 100644 --- a/packages/website/ts/containers/connect_documentation.tsx +++ b/packages/website/ts/containers/connect_documentation.tsx @@ -16,81 +16,81 @@ const InstallationMarkdown = require('md/docs/connect/installation'); /* tslint:enable:no-var-requires */ const connectDocSections = { - introduction: 'introduction', - installation: 'installation', - httpClient: 'httpClient', - webSocketOrderbookChannel: 'webSocketOrderbookChannel', - types: constants.TYPES_SECTION_NAME, + introduction: 'introduction', + installation: 'installation', + httpClient: 'httpClient', + webSocketOrderbookChannel: 'webSocketOrderbookChannel', + types: constants.TYPES_SECTION_NAME, }; const docsInfoConfig: DocsInfoConfig = { - displayName: '0x Connect', - subPackageName: 'connect', - packageUrl: 'https://github.com/0xProject/0x.js', - websitePath: WebsitePaths.Connect, - docsJsonRoot: 'https://s3.amazonaws.com/connect-docs-jsons', - menu: { - introduction: [connectDocSections.introduction], - install: [connectDocSections.installation], - httpClient: [connectDocSections.httpClient], - webSocketOrderbookChannel: [connectDocSections.webSocketOrderbookChannel], - types: [connectDocSections.types], - }, - sectionNameToMarkdown: { - [connectDocSections.introduction]: IntroMarkdown, - [connectDocSections.installation]: InstallationMarkdown, - }, - // 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: [ - 'Client', - 'FeesRequest', - 'FeesResponse', - 'OrderbookChannel', - 'OrderbookChannelHandler', - 'OrderbookChannelSubscriptionOpts', - 'OrderbookRequest', - 'OrderbookResponse', - 'OrdersRequest', - 'TokenPairsItem', - 'TokenPairsRequest', - 'TokenTradeInfo', - 'Order', - 'SignedOrder', - 'ECSignature', - ], - sectionNameToModulePath: { - [connectDocSections.httpClient]: ['"src/http_client"'], - [connectDocSections.webSocketOrderbookChannel]: ['"src/ws_orderbook_channel"'], - [connectDocSections.types]: ['"src/types"'], - }, - menuSubsectionToVersionWhenIntroduced: {}, - sections: connectDocSections, - visibleConstructors: [connectDocSections.httpClient, connectDocSections.webSocketOrderbookChannel], - convertToDocAgnosticFormatFn: typeDocUtils.convertToDocAgnosticFormat.bind(typeDocUtils), + displayName: '0x Connect', + subPackageName: 'connect', + packageUrl: 'https://github.com/0xProject/0x.js', + websitePath: WebsitePaths.Connect, + docsJsonRoot: 'https://s3.amazonaws.com/connect-docs-jsons', + menu: { + introduction: [connectDocSections.introduction], + install: [connectDocSections.installation], + httpClient: [connectDocSections.httpClient], + webSocketOrderbookChannel: [connectDocSections.webSocketOrderbookChannel], + types: [connectDocSections.types], + }, + sectionNameToMarkdown: { + [connectDocSections.introduction]: IntroMarkdown, + [connectDocSections.installation]: InstallationMarkdown, + }, + // 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: [ + 'Client', + 'FeesRequest', + 'FeesResponse', + 'OrderbookChannel', + 'OrderbookChannelHandler', + 'OrderbookChannelSubscriptionOpts', + 'OrderbookRequest', + 'OrderbookResponse', + 'OrdersRequest', + 'TokenPairsItem', + 'TokenPairsRequest', + 'TokenTradeInfo', + 'Order', + 'SignedOrder', + 'ECSignature', + ], + sectionNameToModulePath: { + [connectDocSections.httpClient]: ['"src/http_client"'], + [connectDocSections.webSocketOrderbookChannel]: ['"src/ws_orderbook_channel"'], + [connectDocSections.types]: ['"src/types"'], + }, + menuSubsectionToVersionWhenIntroduced: {}, + sections: connectDocSections, + visibleConstructors: [connectDocSections.httpClient, connectDocSections.webSocketOrderbookChannel], + convertToDocAgnosticFormatFn: typeDocUtils.convertToDocAgnosticFormat.bind(typeDocUtils), }; const docsInfo = new DocsInfo(docsInfoConfig); interface ConnectedState { - docsVersion: string; - availableDocVersions: string[]; - docsInfo: DocsInfo; + docsVersion: string; + availableDocVersions: string[]; + docsInfo: DocsInfo; } interface ConnectedDispatch { - dispatcher: Dispatcher; + dispatcher: Dispatcher; } const mapStateToProps = (state: State, ownProps: DocumentationAllProps): ConnectedState => ({ - docsVersion: state.docsVersion, - availableDocVersions: state.availableDocVersions, - docsInfo, + docsVersion: state.docsVersion, + availableDocVersions: state.availableDocVersions, + docsInfo, }); const mapDispatchToProps = (dispatch: Dispatch): ConnectedDispatch => ({ - dispatcher: new Dispatcher(dispatch), + dispatcher: new Dispatcher(dispatch), }); export const Documentation: React.ComponentClass = connect(mapStateToProps, mapDispatchToProps)( - DocumentationComponent, + DocumentationComponent, ); diff --git a/packages/website/ts/containers/generate_order_form.tsx b/packages/website/ts/containers/generate_order_form.tsx index c2b781557..3fd31087f 100644 --- a/packages/website/ts/containers/generate_order_form.tsx +++ b/packages/website/ts/containers/generate_order_form.tsx @@ -7,48 +7,48 @@ import { GenerateOrderForm as GenerateOrderFormComponent } from 'ts/components/g import { Dispatcher } from 'ts/redux/dispatcher'; import { State } from 'ts/redux/reducer'; import { - BlockchainErrs, - HashData, - SideToAssetToken, - SignatureData, - TokenByAddress, - TokenStateByAddress, + BlockchainErrs, + HashData, + SideToAssetToken, + SignatureData, + TokenByAddress, + TokenStateByAddress, } from 'ts/types'; interface GenerateOrderFormProps { - blockchain: Blockchain; - hashData: HashData; - dispatcher: Dispatcher; + blockchain: Blockchain; + hashData: HashData; + dispatcher: Dispatcher; } interface ConnectedState { - blockchainErr: BlockchainErrs; - blockchainIsLoaded: boolean; - orderExpiryTimestamp: BigNumber; - orderSignatureData: SignatureData; - userAddress: string; - orderTakerAddress: string; - orderSalt: BigNumber; - networkId: number; - sideToAssetToken: SideToAssetToken; - tokenByAddress: TokenByAddress; - tokenStateByAddress: TokenStateByAddress; + blockchainErr: BlockchainErrs; + blockchainIsLoaded: boolean; + orderExpiryTimestamp: BigNumber; + orderSignatureData: SignatureData; + userAddress: string; + orderTakerAddress: string; + orderSalt: BigNumber; + networkId: number; + sideToAssetToken: SideToAssetToken; + tokenByAddress: TokenByAddress; + tokenStateByAddress: TokenStateByAddress; } const mapStateToProps = (state: State, ownProps: GenerateOrderFormProps): ConnectedState => ({ - blockchainErr: state.blockchainErr, - blockchainIsLoaded: state.blockchainIsLoaded, - orderExpiryTimestamp: state.orderExpiryTimestamp, - orderSignatureData: state.orderSignatureData, - orderTakerAddress: state.orderTakerAddress, - orderSalt: state.orderSalt, - networkId: state.networkId, - sideToAssetToken: state.sideToAssetToken, - tokenByAddress: state.tokenByAddress, - tokenStateByAddress: state.tokenStateByAddress, - userAddress: state.userAddress, + blockchainErr: state.blockchainErr, + blockchainIsLoaded: state.blockchainIsLoaded, + orderExpiryTimestamp: state.orderExpiryTimestamp, + orderSignatureData: state.orderSignatureData, + orderTakerAddress: state.orderTakerAddress, + orderSalt: state.orderSalt, + networkId: state.networkId, + sideToAssetToken: state.sideToAssetToken, + tokenByAddress: state.tokenByAddress, + tokenStateByAddress: state.tokenStateByAddress, + userAddress: state.userAddress, }); export const GenerateOrderForm: React.ComponentClass = connect(mapStateToProps)( - GenerateOrderFormComponent, + GenerateOrderFormComponent, ); diff --git a/packages/website/ts/containers/portal.tsx b/packages/website/ts/containers/portal.tsx index bc7aa213c..f0247935b 100644 --- a/packages/website/ts/containers/portal.tsx +++ b/packages/website/ts/containers/portal.tsx @@ -10,72 +10,72 @@ import { BlockchainErrs, HashData, Order, ScreenWidths, Side, TokenByAddress, To import { constants } from 'ts/utils/constants'; interface ConnectedState { - blockchainErr: BlockchainErrs; - blockchainIsLoaded: boolean; - hashData: HashData; - networkId: number; - nodeVersion: string; - orderFillAmount: BigNumber; - tokenByAddress: TokenByAddress; - tokenStateByAddress: TokenStateByAddress; - userEtherBalance: BigNumber; - screenWidth: ScreenWidths; - shouldBlockchainErrDialogBeOpen: boolean; - userAddress: string; - userSuppliedOrderCache: Order; - flashMessage?: string | React.ReactNode; + blockchainErr: BlockchainErrs; + blockchainIsLoaded: boolean; + hashData: HashData; + networkId: number; + nodeVersion: string; + orderFillAmount: BigNumber; + tokenByAddress: TokenByAddress; + tokenStateByAddress: TokenStateByAddress; + userEtherBalance: BigNumber; + screenWidth: ScreenWidths; + shouldBlockchainErrDialogBeOpen: boolean; + userAddress: string; + userSuppliedOrderCache: Order; + flashMessage?: string | React.ReactNode; } interface ConnectedDispatch { - dispatcher: Dispatcher; + dispatcher: Dispatcher; } const mapStateToProps = (state: State, ownProps: PortalComponentAllProps): ConnectedState => { - const receiveAssetToken = state.sideToAssetToken[Side.Receive]; - const depositAssetToken = state.sideToAssetToken[Side.Deposit]; - const receiveAddress = !_.isUndefined(receiveAssetToken.address) - ? receiveAssetToken.address - : constants.NULL_ADDRESS; - const depositAddress = !_.isUndefined(depositAssetToken.address) - ? depositAssetToken.address - : constants.NULL_ADDRESS; - const receiveAmount = !_.isUndefined(receiveAssetToken.amount) ? receiveAssetToken.amount : new BigNumber(0); - const depositAmount = !_.isUndefined(depositAssetToken.amount) ? depositAssetToken.amount : new BigNumber(0); - const hashData = { - depositAmount, - depositTokenContractAddr: depositAddress, - feeRecipientAddress: constants.NULL_ADDRESS, - makerFee: constants.MAKER_FEE, - orderExpiryTimestamp: state.orderExpiryTimestamp, - orderMakerAddress: state.userAddress, - orderTakerAddress: state.orderTakerAddress !== '' ? state.orderTakerAddress : constants.NULL_ADDRESS, - receiveAmount, - receiveTokenContractAddr: receiveAddress, - takerFee: constants.TAKER_FEE, - orderSalt: state.orderSalt, - }; - return { - blockchainErr: state.blockchainErr, - blockchainIsLoaded: state.blockchainIsLoaded, - networkId: state.networkId, - nodeVersion: state.nodeVersion, - orderFillAmount: state.orderFillAmount, - hashData, - screenWidth: state.screenWidth, - shouldBlockchainErrDialogBeOpen: state.shouldBlockchainErrDialogBeOpen, - tokenByAddress: state.tokenByAddress, - tokenStateByAddress: state.tokenStateByAddress, - userAddress: state.userAddress, - userEtherBalance: state.userEtherBalance, - userSuppliedOrderCache: state.userSuppliedOrderCache, - flashMessage: state.flashMessage, - }; + const receiveAssetToken = state.sideToAssetToken[Side.Receive]; + const depositAssetToken = state.sideToAssetToken[Side.Deposit]; + const receiveAddress = !_.isUndefined(receiveAssetToken.address) + ? receiveAssetToken.address + : constants.NULL_ADDRESS; + const depositAddress = !_.isUndefined(depositAssetToken.address) + ? depositAssetToken.address + : constants.NULL_ADDRESS; + const receiveAmount = !_.isUndefined(receiveAssetToken.amount) ? receiveAssetToken.amount : new BigNumber(0); + const depositAmount = !_.isUndefined(depositAssetToken.amount) ? depositAssetToken.amount : new BigNumber(0); + const hashData = { + depositAmount, + depositTokenContractAddr: depositAddress, + feeRecipientAddress: constants.NULL_ADDRESS, + makerFee: constants.MAKER_FEE, + orderExpiryTimestamp: state.orderExpiryTimestamp, + orderMakerAddress: state.userAddress, + orderTakerAddress: state.orderTakerAddress !== '' ? state.orderTakerAddress : constants.NULL_ADDRESS, + receiveAmount, + receiveTokenContractAddr: receiveAddress, + takerFee: constants.TAKER_FEE, + orderSalt: state.orderSalt, + }; + return { + blockchainErr: state.blockchainErr, + blockchainIsLoaded: state.blockchainIsLoaded, + networkId: state.networkId, + nodeVersion: state.nodeVersion, + orderFillAmount: state.orderFillAmount, + hashData, + screenWidth: state.screenWidth, + shouldBlockchainErrDialogBeOpen: state.shouldBlockchainErrDialogBeOpen, + tokenByAddress: state.tokenByAddress, + tokenStateByAddress: state.tokenStateByAddress, + userAddress: state.userAddress, + userEtherBalance: state.userEtherBalance, + userSuppliedOrderCache: state.userSuppliedOrderCache, + flashMessage: state.flashMessage, + }; }; const mapDispatchToProps = (dispatch: Dispatch): ConnectedDispatch => ({ - dispatcher: new Dispatcher(dispatch), + dispatcher: new Dispatcher(dispatch), }); export const Portal: React.ComponentClass = connect(mapStateToProps, mapDispatchToProps)( - PortalComponent, + PortalComponent, ); diff --git a/packages/website/ts/containers/smart_contracts_documentation.tsx b/packages/website/ts/containers/smart_contracts_documentation.tsx index addfd1af9..8be33b546 100644 --- a/packages/website/ts/containers/smart_contracts_documentation.tsx +++ b/packages/website/ts/containers/smart_contracts_documentation.tsx @@ -14,49 +14,49 @@ const IntroMarkdown = require('md/docs/smart_contracts/introduction'); /* tslint:enable:no-var-requires */ const docsInfoConfig: DocsInfoConfig = { - displayName: '0x Smart Contracts', - packageUrl: 'https://github.com/0xProject/contracts', - websitePath: WebsitePaths.SmartContracts, - docsJsonRoot: 'https://s3.amazonaws.com/smart-contracts-docs-json', - menu: { - introduction: [Sections.Introduction], - contracts: [Sections.Exchange, Sections.TokenRegistry, Sections.ZRXToken, Sections.TokenTransferProxy], - }, - sectionNameToMarkdown: { - [Sections.Introduction]: IntroMarkdown, - }, - sections: { - Introduction: Sections.Introduction, - Exchange: Sections.Exchange, - TokenTransferProxy: Sections.TokenTransferProxy, - TokenRegistry: Sections.TokenRegistry, - ZRXToken: Sections.ZRXToken, - }, - visibleConstructors: [Sections.Exchange, Sections.TokenRegistry, Sections.ZRXToken, Sections.TokenTransferProxy], - convertToDocAgnosticFormatFn: doxityUtils.convertToDocAgnosticFormat.bind(doxityUtils), + displayName: '0x Smart Contracts', + packageUrl: 'https://github.com/0xProject/contracts', + websitePath: WebsitePaths.SmartContracts, + docsJsonRoot: 'https://s3.amazonaws.com/smart-contracts-docs-json', + menu: { + introduction: [Sections.Introduction], + contracts: [Sections.Exchange, Sections.TokenRegistry, Sections.ZRXToken, Sections.TokenTransferProxy], + }, + sectionNameToMarkdown: { + [Sections.Introduction]: IntroMarkdown, + }, + sections: { + Introduction: Sections.Introduction, + Exchange: Sections.Exchange, + TokenTransferProxy: Sections.TokenTransferProxy, + TokenRegistry: Sections.TokenRegistry, + ZRXToken: Sections.ZRXToken, + }, + visibleConstructors: [Sections.Exchange, Sections.TokenRegistry, Sections.ZRXToken, Sections.TokenTransferProxy], + convertToDocAgnosticFormatFn: doxityUtils.convertToDocAgnosticFormat.bind(doxityUtils), }; const docsInfo = new DocsInfo(docsInfoConfig); interface ConnectedState { - docsVersion: string; - availableDocVersions: string[]; + docsVersion: string; + availableDocVersions: string[]; } interface ConnectedDispatch { - dispatcher: Dispatcher; - docsInfo: DocsInfo; + dispatcher: Dispatcher; + docsInfo: DocsInfo; } const mapStateToProps = (state: State, ownProps: DocumentationAllProps): ConnectedState => ({ - docsVersion: state.docsVersion, - availableDocVersions: state.availableDocVersions, + docsVersion: state.docsVersion, + availableDocVersions: state.availableDocVersions, }); const mapDispatchToProps = (dispatch: Dispatch): ConnectedDispatch => ({ - dispatcher: new Dispatcher(dispatch), - docsInfo, + dispatcher: new Dispatcher(dispatch), + docsInfo, }); export const Documentation: React.ComponentClass = connect(mapStateToProps, mapDispatchToProps)( - DocumentationComponent, + DocumentationComponent, ); diff --git a/packages/website/ts/containers/zero_ex_js_documentation.tsx b/packages/website/ts/containers/zero_ex_js_documentation.tsx index d416cbc12..8ae6a7b73 100644 --- a/packages/website/ts/containers/zero_ex_js_documentation.tsx +++ b/packages/website/ts/containers/zero_ex_js_documentation.tsx @@ -19,152 +19,152 @@ const versioningMarkdown = require('md/docs/0xjs/versioning'); /* tslint:enable:no-var-requires */ const zeroExJsDocSections = { - introduction: 'introduction', - installation: 'installation', - testrpc: 'testrpc', - async: 'async', - errors: 'errors', - versioning: 'versioning', - zeroEx: 'zeroEx', - exchange: 'exchange', - token: 'token', - tokenRegistry: 'tokenRegistry', - etherToken: 'etherToken', - proxy: 'proxy', - orderWatcher: 'orderWatcher', - types: constants.TYPES_SECTION_NAME, + introduction: 'introduction', + installation: 'installation', + testrpc: 'testrpc', + async: 'async', + errors: 'errors', + versioning: 'versioning', + zeroEx: 'zeroEx', + exchange: 'exchange', + token: 'token', + tokenRegistry: 'tokenRegistry', + etherToken: 'etherToken', + proxy: 'proxy', + orderWatcher: 'orderWatcher', + types: constants.TYPES_SECTION_NAME, }; const docsInfoConfig: DocsInfoConfig = { - displayName: '0x.js', - packageUrl: 'https://github.com/0xProject/0x.js', - subPackageName: '0x.js', - websitePath: WebsitePaths.ZeroExJs, - docsJsonRoot: 'https://s3.amazonaws.com/0xjs-docs-jsons', - menu: { - introduction: [zeroExJsDocSections.introduction], - install: [zeroExJsDocSections.installation], - topics: [zeroExJsDocSections.async, zeroExJsDocSections.errors, zeroExJsDocSections.versioning], - zeroEx: [zeroExJsDocSections.zeroEx], - contracts: [ - zeroExJsDocSections.exchange, - zeroExJsDocSections.token, - zeroExJsDocSections.tokenRegistry, - zeroExJsDocSections.etherToken, - zeroExJsDocSections.proxy, - ], - orderWatcher: [zeroExJsDocSections.orderWatcher], - types: [zeroExJsDocSections.types], - }, - sectionNameToMarkdown: { - [zeroExJsDocSections.introduction]: IntroMarkdown, - [zeroExJsDocSections.installation]: InstallationMarkdown, - [zeroExJsDocSections.async]: AsyncMarkdown, - [zeroExJsDocSections.errors]: ErrorsMarkdown, - [zeroExJsDocSections.versioning]: versioningMarkdown, - }, - // 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: [ - 'Order', - 'SignedOrder', - 'ECSignature', - 'ZeroExError', - 'EventCallback', - 'EventCallbackAsync', - 'EventCallbackSync', - 'ExchangeContractErrs', - 'ContractEvent', - 'Token', - 'ExchangeEvents', - 'IndexedFilterValues', - 'SubscriptionOpts', - 'BlockRange', - 'BlockParam', - 'OrderFillOrKillRequest', - 'OrderCancellationRequest', - 'OrderFillRequest', - 'ContractEventEmitter', - 'Web3Provider', - 'ContractEventArgs', - 'LogCancelArgs', - 'LogFillArgs', - 'LogErrorContractEventArgs', - 'LogFillContractEventArgs', - 'LogCancelContractEventArgs', - 'EtherTokenContractEventArgs', - 'WithdrawalContractEventArgs', - 'DepositContractEventArgs', - 'TokenEvents', - 'ExchangeContractEventArgs', - 'TransferContractEventArgs', - 'ApprovalContractEventArgs', - 'TokenContractEventArgs', - 'ZeroExConfig', - 'TransactionReceiptWithDecodedLogs', - 'LogWithDecodedArgs', - 'EtherTokenEvents', - 'BlockParamLiteral', - 'DecodedLogArgs', - 'MethodOpts', - 'ValidateOrderFillableOpts', - 'OrderTransactionOpts', - 'TransactionOpts', - 'ContractEventArg', - 'LogEvent', - 'LogEntry', - 'DecodedLogEvent', - 'EventWatcherCallback', - 'OnOrderStateChangeCallback', - 'OrderStateValid', - 'OrderStateInvalid', - 'OrderState', - 'FilterObject', - ], - sectionNameToModulePath: { - [zeroExJsDocSections.zeroEx]: ['"src/0x"'], - [zeroExJsDocSections.exchange]: ['"src/contract_wrappers/exchange_wrapper"'], - [zeroExJsDocSections.tokenRegistry]: ['"src/contract_wrappers/token_registry_wrapper"'], - [zeroExJsDocSections.token]: ['"src/contract_wrappers/token_wrapper"'], - [zeroExJsDocSections.etherToken]: ['"src/contract_wrappers/ether_token_wrapper"'], - [zeroExJsDocSections.proxy]: [ - '"src/contract_wrappers/proxy_wrapper"', - '"src/contract_wrappers/token_transfer_proxy_wrapper"', - ], - [zeroExJsDocSections.orderWatcher]: ['"src/order_watcher/order_state_watcher"'], - [zeroExJsDocSections.types]: ['"src/types"'], - }, - menuSubsectionToVersionWhenIntroduced: { - [zeroExJsDocSections.etherToken]: '0.7.1', - [zeroExJsDocSections.proxy]: '0.8.0', - [zeroExJsDocSections.orderWatcher]: '0.27.1', - }, - sections: zeroExJsDocSections, - visibleConstructors: [zeroExJsDocSections.zeroEx], - convertToDocAgnosticFormatFn: typeDocUtils.convertToDocAgnosticFormat.bind(typeDocUtils), + displayName: '0x.js', + packageUrl: 'https://github.com/0xProject/0x.js', + subPackageName: '0x.js', + websitePath: WebsitePaths.ZeroExJs, + docsJsonRoot: 'https://s3.amazonaws.com/0xjs-docs-jsons', + menu: { + introduction: [zeroExJsDocSections.introduction], + install: [zeroExJsDocSections.installation], + topics: [zeroExJsDocSections.async, zeroExJsDocSections.errors, zeroExJsDocSections.versioning], + zeroEx: [zeroExJsDocSections.zeroEx], + contracts: [ + zeroExJsDocSections.exchange, + zeroExJsDocSections.token, + zeroExJsDocSections.tokenRegistry, + zeroExJsDocSections.etherToken, + zeroExJsDocSections.proxy, + ], + orderWatcher: [zeroExJsDocSections.orderWatcher], + types: [zeroExJsDocSections.types], + }, + sectionNameToMarkdown: { + [zeroExJsDocSections.introduction]: IntroMarkdown, + [zeroExJsDocSections.installation]: InstallationMarkdown, + [zeroExJsDocSections.async]: AsyncMarkdown, + [zeroExJsDocSections.errors]: ErrorsMarkdown, + [zeroExJsDocSections.versioning]: versioningMarkdown, + }, + // 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: [ + 'Order', + 'SignedOrder', + 'ECSignature', + 'ZeroExError', + 'EventCallback', + 'EventCallbackAsync', + 'EventCallbackSync', + 'ExchangeContractErrs', + 'ContractEvent', + 'Token', + 'ExchangeEvents', + 'IndexedFilterValues', + 'SubscriptionOpts', + 'BlockRange', + 'BlockParam', + 'OrderFillOrKillRequest', + 'OrderCancellationRequest', + 'OrderFillRequest', + 'ContractEventEmitter', + 'Web3Provider', + 'ContractEventArgs', + 'LogCancelArgs', + 'LogFillArgs', + 'LogErrorContractEventArgs', + 'LogFillContractEventArgs', + 'LogCancelContractEventArgs', + 'EtherTokenContractEventArgs', + 'WithdrawalContractEventArgs', + 'DepositContractEventArgs', + 'TokenEvents', + 'ExchangeContractEventArgs', + 'TransferContractEventArgs', + 'ApprovalContractEventArgs', + 'TokenContractEventArgs', + 'ZeroExConfig', + 'TransactionReceiptWithDecodedLogs', + 'LogWithDecodedArgs', + 'EtherTokenEvents', + 'BlockParamLiteral', + 'DecodedLogArgs', + 'MethodOpts', + 'ValidateOrderFillableOpts', + 'OrderTransactionOpts', + 'TransactionOpts', + 'ContractEventArg', + 'LogEvent', + 'LogEntry', + 'DecodedLogEvent', + 'EventWatcherCallback', + 'OnOrderStateChangeCallback', + 'OrderStateValid', + 'OrderStateInvalid', + 'OrderState', + 'FilterObject', + ], + sectionNameToModulePath: { + [zeroExJsDocSections.zeroEx]: ['"src/0x"'], + [zeroExJsDocSections.exchange]: ['"src/contract_wrappers/exchange_wrapper"'], + [zeroExJsDocSections.tokenRegistry]: ['"src/contract_wrappers/token_registry_wrapper"'], + [zeroExJsDocSections.token]: ['"src/contract_wrappers/token_wrapper"'], + [zeroExJsDocSections.etherToken]: ['"src/contract_wrappers/ether_token_wrapper"'], + [zeroExJsDocSections.proxy]: [ + '"src/contract_wrappers/proxy_wrapper"', + '"src/contract_wrappers/token_transfer_proxy_wrapper"', + ], + [zeroExJsDocSections.orderWatcher]: ['"src/order_watcher/order_state_watcher"'], + [zeroExJsDocSections.types]: ['"src/types"'], + }, + menuSubsectionToVersionWhenIntroduced: { + [zeroExJsDocSections.etherToken]: '0.7.1', + [zeroExJsDocSections.proxy]: '0.8.0', + [zeroExJsDocSections.orderWatcher]: '0.27.1', + }, + sections: zeroExJsDocSections, + visibleConstructors: [zeroExJsDocSections.zeroEx], + convertToDocAgnosticFormatFn: typeDocUtils.convertToDocAgnosticFormat.bind(typeDocUtils), }; const docsInfo = new DocsInfo(docsInfoConfig); interface ConnectedState { - docsVersion: string; - availableDocVersions: string[]; - docsInfo: DocsInfo; + docsVersion: string; + availableDocVersions: string[]; + docsInfo: DocsInfo; } interface ConnectedDispatch { - dispatcher: Dispatcher; + dispatcher: Dispatcher; } const mapStateToProps = (state: State, ownProps: DocumentationAllProps): ConnectedState => ({ - docsVersion: state.docsVersion, - availableDocVersions: state.availableDocVersions, - docsInfo, + docsVersion: state.docsVersion, + availableDocVersions: state.availableDocVersions, + docsInfo, }); const mapDispatchToProps = (dispatch: Dispatch): ConnectedDispatch => ({ - dispatcher: new Dispatcher(dispatch), + dispatcher: new Dispatcher(dispatch), }); export const Documentation: React.ComponentClass = connect(mapStateToProps, mapDispatchToProps)( - DocumentationComponent, + DocumentationComponent, ); diff --git a/packages/website/ts/globals.d.ts b/packages/website/ts/globals.d.ts index 736b23d9d..383e5cbe0 100644 --- a/packages/website/ts/globals.d.ts +++ b/packages/website/ts/globals.d.ts @@ -14,10 +14,10 @@ declare module 'ledgerco'; declare module 'ethereumjs-tx'; declare module '*.json' { - const json: any; - /* tslint:disable */ - export default json; - /* tslint:enable */ + const json: any; + /* tslint:disable */ + export default json; + /* tslint:enable */ } // tslint:disable:max-classes-per-file @@ -25,137 +25,137 @@ declare module '*.json' { // find-version declarations declare function findVersions(version: string): string[]; declare module 'find-versions' { - export = findVersions; + export = findVersions; } // compare-version declarations declare function compareVersions(firstVersion: string, secondVersion: string): number; declare module 'compare-versions' { - export = compareVersions; + export = compareVersions; } // semver-sort declarations declare module 'semver-sort' { - const desc: (versions: string[]) => string[]; + const desc: (versions: string[]) => string[]; } // xml-js declarations declare interface XML2JSONOpts { - compact?: boolean; - spaces?: number; + compact?: boolean; + spaces?: number; } declare module 'xml-js' { - const xml2json: (xml: string, opts: XML2JSONOpts) => string; + const xml2json: (xml: string, opts: XML2JSONOpts) => string; } // This will be defined by default in TS 2.4 // Source: https://github.com/Microsoft/TypeScript/issues/12364 interface System { - import(module: string): Promise; + import(module: string): Promise; } declare var System: System; // jsonschema declarations // Source: https://github.com/tdegrunt/jsonschema/blob/master/lib/index.d.ts declare interface Schema { - id?: string; - $schema?: string; - title?: string; - description?: string; - multipleOf?: number; - maximum?: number; - exclusiveMaximum?: boolean; - minimum?: number; - exclusiveMinimum?: boolean; - maxLength?: number; - minLength?: number; - pattern?: string; - additionalItems?: boolean | Schema; - items?: Schema | Schema[]; - maxItems?: number; - minItems?: number; - uniqueItems?: boolean; - maxProperties?: number; - minProperties?: number; - required?: string[]; - additionalProperties?: boolean | Schema; - definitions?: { - [name: string]: Schema; - }; - properties?: { - [name: string]: Schema; - }; - patternProperties?: { - [name: string]: Schema; - }; - dependencies?: { - [name: string]: Schema | string[]; - }; - enum?: any[]; - type?: string | string[]; - allOf?: Schema[]; - anyOf?: Schema[]; - oneOf?: Schema[]; - not?: Schema; - // This is the only property that's not defined in https://github.com/tdegrunt/jsonschema/blob/master/lib/index.d.ts - // There is an open issue for that: https://github.com/tdegrunt/jsonschema/issues/194 - // There is also an opened PR: https://github.com/tdegrunt/jsonschema/pull/218/files - // As soon as it gets merged we should be good to use types from 'jsonschema' package - $ref?: string; + id?: string; + $schema?: string; + title?: string; + description?: string; + multipleOf?: number; + maximum?: number; + exclusiveMaximum?: boolean; + minimum?: number; + exclusiveMinimum?: boolean; + maxLength?: number; + minLength?: number; + pattern?: string; + additionalItems?: boolean | Schema; + items?: Schema | Schema[]; + maxItems?: number; + minItems?: number; + uniqueItems?: boolean; + maxProperties?: number; + minProperties?: number; + required?: string[]; + additionalProperties?: boolean | Schema; + definitions?: { + [name: string]: Schema; + }; + properties?: { + [name: string]: Schema; + }; + patternProperties?: { + [name: string]: Schema; + }; + dependencies?: { + [name: string]: Schema | string[]; + }; + enum?: any[]; + type?: string | string[]; + allOf?: Schema[]; + anyOf?: Schema[]; + oneOf?: Schema[]; + not?: Schema; + // This is the only property that's not defined in https://github.com/tdegrunt/jsonschema/blob/master/lib/index.d.ts + // There is an open issue for that: https://github.com/tdegrunt/jsonschema/issues/194 + // There is also an opened PR: https://github.com/tdegrunt/jsonschema/pull/218/files + // As soon as it gets merged we should be good to use types from 'jsonschema' package + $ref?: string; } // blockies declarations declare interface BlockiesIcon { - toDataURL(): string; + toDataURL(): string; } declare interface BlockiesConfig { - seed: string; + seed: string; } declare function blockies(config: BlockiesConfig): BlockiesIcon; declare module 'blockies' { - export = blockies; + export = blockies; } // is-mobile declarations declare function isMobile(): boolean; declare module 'is-mobile' { - export = isMobile; + export = isMobile; } // web3-provider-engine declarations declare class Subprovider {} declare module 'web3-provider-engine/subproviders/subprovider' { - export = Subprovider; + export = Subprovider; } declare module 'web3-provider-engine/subproviders/rpc' { - import * as Web3 from 'web3'; - class RpcSubprovider { - constructor(options: { rpcUrl: string }); - public handleRequest( - payload: Web3.JSONRPCRequestPayload, - next: () => void, - end: (err: Error | null, data?: any) => void, - ): void; - } - export = RpcSubprovider; + import * as Web3 from 'web3'; + class RpcSubprovider { + constructor(options: { rpcUrl: string }); + public handleRequest( + payload: Web3.JSONRPCRequestPayload, + next: () => void, + end: (err: Error | null, data?: any) => void, + ): void; + } + export = RpcSubprovider; } declare module 'web3-provider-engine' { - class Web3ProviderEngine { - public on(event: string, handler: () => void): void; - public send(payload: any): void; - public sendAsync(payload: any, callback: (error: any, response: any) => void): void; - public addProvider(provider: any): void; - public start(): void; - public stop(): void; - } - export = Web3ProviderEngine; + class Web3ProviderEngine { + public on(event: string, handler: () => void): void; + public send(payload: any): void; + public sendAsync(payload: any, callback: (error: any, response: any) => void): void; + public addProvider(provider: any): void; + public start(): void; + public stop(): void; + } + export = Web3ProviderEngine; } declare interface Artifact { - abi: any; - networks: { - [networkId: number]: { - address: string; - }; - }; + abi: any; + networks: { + [networkId: number]: { + address: string; + }; + }; } diff --git a/packages/website/ts/index.tsx b/packages/website/ts/index.tsx index df45c80bd..ffb551561 100644 --- a/packages/website/ts/index.tsx +++ b/packages/website/ts/index.tsx @@ -32,44 +32,44 @@ import 'less/all.less'; // At the same time webpack statically parses for System.import() to determine bundle chunk split points // so each lazy import needs it's own `System.import()` declaration. const LazyPortal = createLazyComponent('Portal', async () => - System.import(/* webpackChunkName: "portal" */ 'ts/containers/portal'), + System.import(/* webpackChunkName: "portal" */ 'ts/containers/portal'), ); const LazyZeroExJSDocumentation = createLazyComponent('Documentation', async () => - System.import(/* webpackChunkName: "zeroExDocs" */ 'ts/containers/zero_ex_js_documentation'), + System.import(/* webpackChunkName: "zeroExDocs" */ 'ts/containers/zero_ex_js_documentation'), ); const LazySmartContractsDocumentation = createLazyComponent('Documentation', async () => - System.import(/* webpackChunkName: "smartContractDocs" */ 'ts/containers/smart_contracts_documentation'), + System.import(/* webpackChunkName: "smartContractDocs" */ 'ts/containers/smart_contracts_documentation'), ); const LazyConnectDocumentation = createLazyComponent('Documentation', async () => - System.import(/* webpackChunkName: "connectDocs" */ 'ts/containers/connect_documentation'), + System.import(/* webpackChunkName: "connectDocs" */ 'ts/containers/connect_documentation'), ); const store: ReduxStore = createStore(reducer); render( - -
- - -
- - - - - - - - - - - - -
-
-
-
-
, - document.getElementById('app'), + +
+ + +
+ + + + + + + + + + + + +
+
+
+
+
, + document.getElementById('app'), ); diff --git a/packages/website/ts/lazy_component.tsx b/packages/website/ts/lazy_component.tsx index 5efebb667..48800c2dd 100644 --- a/packages/website/ts/lazy_component.tsx +++ b/packages/website/ts/lazy_component.tsx @@ -2,12 +2,12 @@ import * as _ from 'lodash'; import * as React from 'react'; interface LazyComponentProps { - reactComponentPromise: Promise>; - reactComponentProps: any; + reactComponentPromise: Promise>; + reactComponentProps: any; } interface LazyComponentState { - component?: React.ComponentClass; + component?: React.ComponentClass; } /** @@ -15,33 +15,33 @@ interface LazyComponentState { * Source: https://reacttraining.com/react-router/web/guides/code-splitting */ export class LazyComponent extends React.Component { - constructor(props: LazyComponentProps) { - super(props); - this.state = { - component: undefined, - }; - } - public componentWillMount() { - // tslint:disable-next-line:no-floating-promises - this._loadComponentFireAndForgetAsync(this.props); - } - public componentWillReceiveProps(nextProps: LazyComponentProps) { - if (nextProps.reactComponentPromise !== this.props.reactComponentPromise) { - // tslint:disable-next-line:no-floating-promises - this._loadComponentFireAndForgetAsync(nextProps); - } - } - public render() { - return _.isUndefined(this.state.component) - ? null - : React.createElement(this.state.component, this.props.reactComponentProps); - } - private async _loadComponentFireAndForgetAsync(props: LazyComponentProps) { - const component = await props.reactComponentPromise; - this.setState({ - component, - }); - } + constructor(props: LazyComponentProps) { + super(props); + this.state = { + component: undefined, + }; + } + public componentWillMount() { + // tslint:disable-next-line:no-floating-promises + this._loadComponentFireAndForgetAsync(this.props); + } + public componentWillReceiveProps(nextProps: LazyComponentProps) { + if (nextProps.reactComponentPromise !== this.props.reactComponentPromise) { + // tslint:disable-next-line:no-floating-promises + this._loadComponentFireAndForgetAsync(nextProps); + } + } + public render() { + return _.isUndefined(this.state.component) + ? null + : React.createElement(this.state.component, this.props.reactComponentProps); + } + private async _loadComponentFireAndForgetAsync(props: LazyComponentProps) { + const component = await props.reactComponentPromise; + this.setState({ + component, + }); + } } /** @@ -52,15 +52,15 @@ export class LazyComponent extends React.Component System.import('ts/containers/portal'));`` */ export const createLazyComponent = (componentName: string, lazyImport: () => Promise) => { - return (props: any) => { - const reactComponentPromise = (async (): Promise> => { - const mod = await lazyImport(); - const component = mod[componentName]; - if (_.isUndefined(component)) { - throw new Error(`Did not find exported component: ${componentName}`); - } - return component; - })(); - return ; - }; + return (props: any) => { + const reactComponentPromise = (async (): Promise> => { + const mod = await lazyImport(); + const component = mod[componentName]; + if (_.isUndefined(component)) { + throw new Error(`Did not find exported component: ${componentName}`); + } + return component; + })(); + return ; + }; }; diff --git a/packages/website/ts/local_storage/local_storage.ts b/packages/website/ts/local_storage/local_storage.ts index 4e5b77a0a..d94e6877b 100644 --- a/packages/website/ts/local_storage/local_storage.ts +++ b/packages/website/ts/local_storage/local_storage.ts @@ -1,35 +1,35 @@ import * as _ from 'lodash'; export const localStorage = { - doesExist() { - return !!window.localStorage; - }, - getItemIfExists(key: string): string { - if (!this.doesExist) { - return undefined; - } - const item = window.localStorage.getItem(key); - if (_.isNull(item) || item === 'undefined') { - return ''; - } - return item; - }, - setItem(key: string, value: string) { - if (!this.doesExist || _.isUndefined(value)) { - return; - } - window.localStorage.setItem(key, value); - }, - removeItem(key: string) { - if (!this.doesExist) { - return; - } - window.localStorage.removeItem(key); - }, - getAllKeys(): string[] { - if (!this.doesExist) { - return []; - } - return _.keys(window.localStorage); - }, + doesExist() { + return !!window.localStorage; + }, + getItemIfExists(key: string): string { + if (!this.doesExist) { + return undefined; + } + const item = window.localStorage.getItem(key); + if (_.isNull(item) || item === 'undefined') { + return ''; + } + return item; + }, + setItem(key: string, value: string) { + if (!this.doesExist || _.isUndefined(value)) { + return; + } + window.localStorage.setItem(key, value); + }, + removeItem(key: string) { + if (!this.doesExist) { + return; + } + window.localStorage.removeItem(key); + }, + getAllKeys(): string[] { + if (!this.doesExist) { + return []; + } + return _.keys(window.localStorage); + }, }; diff --git a/packages/website/ts/local_storage/tracked_token_storage.ts b/packages/website/ts/local_storage/tracked_token_storage.ts index 2c62084e0..7733e8436 100644 --- a/packages/website/ts/local_storage/tracked_token_storage.ts +++ b/packages/website/ts/local_storage/tracked_token_storage.ts @@ -7,61 +7,61 @@ const TRACKED_TOKENS_KEY = 'trackedTokens'; const TRACKED_TOKENS_CLEAR_KEY = 'lastClearTrackedTokensDate'; export const trackedTokenStorage = { - // Clear trackedTokens localStorage if we've updated the config variable in an update - // that introduced a backward incompatible change requiring the tracked tokens to be re-set - clearIfRequired(): void { - const lastClearFillDate = localStorage.getItemIfExists(TRACKED_TOKENS_CLEAR_KEY); - if (lastClearFillDate !== configs.LAST_LOCAL_STORAGE_TRACKED_TOKEN_CLEARANCE_DATE) { - localStorage.removeItem(TRACKED_TOKENS_KEY); - } - localStorage.setItem(TRACKED_TOKENS_CLEAR_KEY, configs.LAST_LOCAL_STORAGE_TRACKED_TOKEN_CLEARANCE_DATE); - }, - addTrackedTokenToUser(userAddress: string, networkId: number, token: Token): void { - const trackedTokensByUserAddress = this.getTrackedTokensByUserAddress(); - let trackedTokensByNetworkId = trackedTokensByUserAddress[userAddress]; - if (_.isUndefined(trackedTokensByNetworkId)) { - trackedTokensByNetworkId = {}; - } - const trackedTokens = !_.isUndefined(trackedTokensByNetworkId[networkId]) - ? trackedTokensByNetworkId[networkId] - : []; - trackedTokens.push(token); - trackedTokensByNetworkId[networkId] = trackedTokens; - trackedTokensByUserAddress[userAddress] = trackedTokensByNetworkId; - const trackedTokensByUserAddressJSONString = JSON.stringify(trackedTokensByUserAddress); - localStorage.setItem(TRACKED_TOKENS_KEY, trackedTokensByUserAddressJSONString); - }, - getTrackedTokensByUserAddress(): TrackedTokensByUserAddress { - const trackedTokensJSONString = localStorage.getItemIfExists(TRACKED_TOKENS_KEY); - if (_.isEmpty(trackedTokensJSONString)) { - return {}; - } - const trackedTokensByUserAddress = JSON.parse(trackedTokensJSONString); - return trackedTokensByUserAddress; - }, - getTrackedTokensIfExists(userAddress: string, networkId: number): Token[] { - const trackedTokensJSONString = localStorage.getItemIfExists(TRACKED_TOKENS_KEY); - if (_.isEmpty(trackedTokensJSONString)) { - return undefined; - } - const trackedTokensByUserAddress = JSON.parse(trackedTokensJSONString); - const trackedTokensByNetworkId = trackedTokensByUserAddress[userAddress]; - if (_.isUndefined(trackedTokensByNetworkId)) { - return undefined; - } - const trackedTokens = trackedTokensByNetworkId[networkId]; - return trackedTokens; - }, - removeTrackedToken(userAddress: string, networkId: number, tokenAddress: string): void { - const trackedTokensByUserAddress = this.getTrackedTokensByUserAddress(); - const trackedTokensByNetworkId = trackedTokensByUserAddress[userAddress]; - const trackedTokens = trackedTokensByNetworkId[networkId]; - const remainingTrackedTokens = _.filter(trackedTokens, (token: Token) => { - return token.address !== tokenAddress; - }); - trackedTokensByNetworkId[networkId] = remainingTrackedTokens; - trackedTokensByUserAddress[userAddress] = trackedTokensByNetworkId; - const trackedTokensByUserAddressJSONString = JSON.stringify(trackedTokensByUserAddress); - localStorage.setItem(TRACKED_TOKENS_KEY, trackedTokensByUserAddressJSONString); - }, + // Clear trackedTokens localStorage if we've updated the config variable in an update + // that introduced a backward incompatible change requiring the tracked tokens to be re-set + clearIfRequired(): void { + const lastClearFillDate = localStorage.getItemIfExists(TRACKED_TOKENS_CLEAR_KEY); + if (lastClearFillDate !== configs.LAST_LOCAL_STORAGE_TRACKED_TOKEN_CLEARANCE_DATE) { + localStorage.removeItem(TRACKED_TOKENS_KEY); + } + localStorage.setItem(TRACKED_TOKENS_CLEAR_KEY, configs.LAST_LOCAL_STORAGE_TRACKED_TOKEN_CLEARANCE_DATE); + }, + addTrackedTokenToUser(userAddress: string, networkId: number, token: Token): void { + const trackedTokensByUserAddress = this.getTrackedTokensByUserAddress(); + let trackedTokensByNetworkId = trackedTokensByUserAddress[userAddress]; + if (_.isUndefined(trackedTokensByNetworkId)) { + trackedTokensByNetworkId = {}; + } + const trackedTokens = !_.isUndefined(trackedTokensByNetworkId[networkId]) + ? trackedTokensByNetworkId[networkId] + : []; + trackedTokens.push(token); + trackedTokensByNetworkId[networkId] = trackedTokens; + trackedTokensByUserAddress[userAddress] = trackedTokensByNetworkId; + const trackedTokensByUserAddressJSONString = JSON.stringify(trackedTokensByUserAddress); + localStorage.setItem(TRACKED_TOKENS_KEY, trackedTokensByUserAddressJSONString); + }, + getTrackedTokensByUserAddress(): TrackedTokensByUserAddress { + const trackedTokensJSONString = localStorage.getItemIfExists(TRACKED_TOKENS_KEY); + if (_.isEmpty(trackedTokensJSONString)) { + return {}; + } + const trackedTokensByUserAddress = JSON.parse(trackedTokensJSONString); + return trackedTokensByUserAddress; + }, + getTrackedTokensIfExists(userAddress: string, networkId: number): Token[] { + const trackedTokensJSONString = localStorage.getItemIfExists(TRACKED_TOKENS_KEY); + if (_.isEmpty(trackedTokensJSONString)) { + return undefined; + } + const trackedTokensByUserAddress = JSON.parse(trackedTokensJSONString); + const trackedTokensByNetworkId = trackedTokensByUserAddress[userAddress]; + if (_.isUndefined(trackedTokensByNetworkId)) { + return undefined; + } + const trackedTokens = trackedTokensByNetworkId[networkId]; + return trackedTokens; + }, + removeTrackedToken(userAddress: string, networkId: number, tokenAddress: string): void { + const trackedTokensByUserAddress = this.getTrackedTokensByUserAddress(); + const trackedTokensByNetworkId = trackedTokensByUserAddress[userAddress]; + const trackedTokens = trackedTokensByNetworkId[networkId]; + const remainingTrackedTokens = _.filter(trackedTokens, (token: Token) => { + return token.address !== tokenAddress; + }); + trackedTokensByNetworkId[networkId] = remainingTrackedTokens; + trackedTokensByUserAddress[userAddress] = trackedTokensByNetworkId; + const trackedTokensByUserAddressJSONString = JSON.stringify(trackedTokensByUserAddress); + localStorage.setItem(TRACKED_TOKENS_KEY, trackedTokensByUserAddressJSONString); + }, }; diff --git a/packages/website/ts/local_storage/trade_history_storage.tsx b/packages/website/ts/local_storage/trade_history_storage.tsx index b20244b29..df731236e 100644 --- a/packages/website/ts/local_storage/trade_history_storage.tsx +++ b/packages/website/ts/local_storage/trade_history_storage.tsx @@ -11,84 +11,84 @@ const FILLS_LATEST_BLOCK = 'fillsLatestBlock'; const FILL_CLEAR_KEY = 'lastClearFillDate'; export const tradeHistoryStorage = { - // Clear all fill related localStorage if we've updated the config variable in an update - // that introduced a backward incompatible change requiring the user to re-fetch the fills from - // the blockchain - clearIfRequired() { - const lastClearFillDate = localStorage.getItemIfExists(FILL_CLEAR_KEY); - if (lastClearFillDate !== configs.LAST_LOCAL_STORAGE_FILL_CLEARANCE_DATE) { - const localStorageKeys = localStorage.getAllKeys(); - _.each(localStorageKeys, key => { - if (_.startsWith(key, `${FILLS_KEY}-`) || _.startsWith(key, `${FILLS_LATEST_BLOCK}-`)) { - localStorage.removeItem(key); - } - }); - } - localStorage.setItem(FILL_CLEAR_KEY, configs.LAST_LOCAL_STORAGE_FILL_CLEARANCE_DATE); - }, - addFillToUser(userAddress: string, networkId: number, fill: Fill) { - const fillsByHash = this.getUserFillsByHash(userAddress, networkId); - const fillHash = this._getFillHash(fill); - const doesFillExist = !_.isUndefined(fillsByHash[fillHash]); - if (doesFillExist) { - return; // noop - } - fillsByHash[fillHash] = fill; - const userFillsJSONString = JSON.stringify(fillsByHash); - const userFillsKey = this._getUserFillsKey(userAddress, networkId); - localStorage.setItem(userFillsKey, userFillsJSONString); - }, - removeFillFromUser(userAddress: string, networkId: number, fill: Fill) { - const fillsByHash = this.getUserFillsByHash(userAddress, networkId); - const fillHash = this._getFillHash(fill); - const doesFillExist = !_.isUndefined(fillsByHash[fillHash]); - if (!doesFillExist) { - return; // noop - } - delete fillsByHash[fillHash]; - const userFillsJSONString = JSON.stringify(fillsByHash); - const userFillsKey = this._getUserFillsKey(userAddress, networkId); - localStorage.setItem(userFillsKey, userFillsJSONString); - }, - getUserFillsByHash(userAddress: string, networkId: number): { [fillHash: string]: Fill } { - const userFillsKey = this._getUserFillsKey(userAddress, networkId); - const userFillsJSONString = localStorage.getItemIfExists(userFillsKey); - if (_.isEmpty(userFillsJSONString)) { - return {}; - } - const userFillsByHash = JSON.parse(userFillsJSONString); - _.each(userFillsByHash, (fill, hash) => { - fill.paidMakerFee = new BigNumber(fill.paidMakerFee); - fill.paidTakerFee = new BigNumber(fill.paidTakerFee); - fill.filledTakerTokenAmount = new BigNumber(fill.filledTakerTokenAmount); - fill.filledMakerTokenAmount = new BigNumber(fill.filledMakerTokenAmount); - }); - return userFillsByHash; - }, - getFillsLatestBlock(userAddress: string, networkId: number): number { - const userFillsLatestBlockKey = this._getFillsLatestBlockKey(userAddress, networkId); - const blockNumberStr = localStorage.getItemIfExists(userFillsLatestBlockKey); - if (_.isEmpty(blockNumberStr)) { - return constants.GENESIS_ORDER_BLOCK_BY_NETWORK_ID[networkId]; - } - const blockNumber = _.parseInt(blockNumberStr); - return blockNumber; - }, - setFillsLatestBlock(userAddress: string, networkId: number, blockNumber: number) { - const userFillsLatestBlockKey = this._getFillsLatestBlockKey(userAddress, networkId); - localStorage.setItem(userFillsLatestBlockKey, `${blockNumber}`); - }, - _getUserFillsKey(userAddress: string, networkId: number) { - const userFillsKey = `${FILLS_KEY}-${userAddress}-${networkId}`; - return userFillsKey; - }, - _getFillsLatestBlockKey(userAddress: string, networkId: number) { - const userFillsLatestBlockKey = `${FILLS_LATEST_BLOCK}-${userAddress}-${networkId}`; - return userFillsLatestBlockKey; - }, - _getFillHash(fill: Fill): string { - const fillJSON = JSON.stringify(fill); - const fillHash = ethUtil.sha256(fillJSON); - return fillHash.toString('hex'); - }, + // Clear all fill related localStorage if we've updated the config variable in an update + // that introduced a backward incompatible change requiring the user to re-fetch the fills from + // the blockchain + clearIfRequired() { + const lastClearFillDate = localStorage.getItemIfExists(FILL_CLEAR_KEY); + if (lastClearFillDate !== configs.LAST_LOCAL_STORAGE_FILL_CLEARANCE_DATE) { + const localStorageKeys = localStorage.getAllKeys(); + _.each(localStorageKeys, key => { + if (_.startsWith(key, `${FILLS_KEY}-`) || _.startsWith(key, `${FILLS_LATEST_BLOCK}-`)) { + localStorage.removeItem(key); + } + }); + } + localStorage.setItem(FILL_CLEAR_KEY, configs.LAST_LOCAL_STORAGE_FILL_CLEARANCE_DATE); + }, + addFillToUser(userAddress: string, networkId: number, fill: Fill) { + const fillsByHash = this.getUserFillsByHash(userAddress, networkId); + const fillHash = this._getFillHash(fill); + const doesFillExist = !_.isUndefined(fillsByHash[fillHash]); + if (doesFillExist) { + return; // noop + } + fillsByHash[fillHash] = fill; + const userFillsJSONString = JSON.stringify(fillsByHash); + const userFillsKey = this._getUserFillsKey(userAddress, networkId); + localStorage.setItem(userFillsKey, userFillsJSONString); + }, + removeFillFromUser(userAddress: string, networkId: number, fill: Fill) { + const fillsByHash = this.getUserFillsByHash(userAddress, networkId); + const fillHash = this._getFillHash(fill); + const doesFillExist = !_.isUndefined(fillsByHash[fillHash]); + if (!doesFillExist) { + return; // noop + } + delete fillsByHash[fillHash]; + const userFillsJSONString = JSON.stringify(fillsByHash); + const userFillsKey = this._getUserFillsKey(userAddress, networkId); + localStorage.setItem(userFillsKey, userFillsJSONString); + }, + getUserFillsByHash(userAddress: string, networkId: number): { [fillHash: string]: Fill } { + const userFillsKey = this._getUserFillsKey(userAddress, networkId); + const userFillsJSONString = localStorage.getItemIfExists(userFillsKey); + if (_.isEmpty(userFillsJSONString)) { + return {}; + } + const userFillsByHash = JSON.parse(userFillsJSONString); + _.each(userFillsByHash, (fill, hash) => { + fill.paidMakerFee = new BigNumber(fill.paidMakerFee); + fill.paidTakerFee = new BigNumber(fill.paidTakerFee); + fill.filledTakerTokenAmount = new BigNumber(fill.filledTakerTokenAmount); + fill.filledMakerTokenAmount = new BigNumber(fill.filledMakerTokenAmount); + }); + return userFillsByHash; + }, + getFillsLatestBlock(userAddress: string, networkId: number): number { + const userFillsLatestBlockKey = this._getFillsLatestBlockKey(userAddress, networkId); + const blockNumberStr = localStorage.getItemIfExists(userFillsLatestBlockKey); + if (_.isEmpty(blockNumberStr)) { + return constants.GENESIS_ORDER_BLOCK_BY_NETWORK_ID[networkId]; + } + const blockNumber = _.parseInt(blockNumberStr); + return blockNumber; + }, + setFillsLatestBlock(userAddress: string, networkId: number, blockNumber: number) { + const userFillsLatestBlockKey = this._getFillsLatestBlockKey(userAddress, networkId); + localStorage.setItem(userFillsLatestBlockKey, `${blockNumber}`); + }, + _getUserFillsKey(userAddress: string, networkId: number) { + const userFillsKey = `${FILLS_KEY}-${userAddress}-${networkId}`; + return userFillsKey; + }, + _getFillsLatestBlockKey(userAddress: string, networkId: number) { + const userFillsLatestBlockKey = `${FILLS_LATEST_BLOCK}-${userAddress}-${networkId}`; + return userFillsLatestBlockKey; + }, + _getFillHash(fill: Fill): string { + const fillJSON = JSON.stringify(fill); + const fillHash = ethUtil.sha256(fillJSON); + return fillHash.toString('hex'); + }, }; diff --git a/packages/website/ts/pages/about/about.tsx b/packages/website/ts/pages/about/about.tsx index 411456427..c929673f5 100644 --- a/packages/website/ts/pages/about/about.tsx +++ b/packages/website/ts/pages/about/about.tsx @@ -10,239 +10,239 @@ import { constants } from 'ts/utils/constants'; import { utils } from 'ts/utils/utils'; const teamRow1: ProfileInfo[] = [ - { - name: 'Will Warren', - title: 'Co-founder & CEO', - description: `Smart contract R&D. Previously applied physics at Los Alamos \ + { + name: 'Will Warren', + title: 'Co-founder & CEO', + description: `Smart contract R&D. Previously applied physics at Los Alamos \ Nat Lab. Mechanical engineering at UC San Diego. PhD dropout.`, - image: '/images/team/will.jpg', - linkedIn: 'https://www.linkedin.com/in/will-warren-92aab62b/', - github: 'https://github.com/willwarren89', - medium: 'https://medium.com/@willwarren89', - }, - { - name: 'Amir Bandeali', - title: 'Co-founder & CTO', - description: `Smart contract R&D. Previously fixed income trader at DRW. \ + image: '/images/team/will.jpg', + linkedIn: 'https://www.linkedin.com/in/will-warren-92aab62b/', + github: 'https://github.com/willwarren89', + medium: 'https://medium.com/@willwarren89', + }, + { + name: 'Amir Bandeali', + title: 'Co-founder & CTO', + description: `Smart contract R&D. Previously fixed income trader at DRW. \ Finance at University of Illinois, Urbana-Champaign.`, - image: '/images/team/amir.jpeg', - linkedIn: 'https://www.linkedin.com/in/abandeali1/', - github: 'https://github.com/abandeali1', - medium: 'https://medium.com/@abandeali1', - }, - { - name: 'Fabio Berger', - title: 'Senior Engineer', - description: `Full-stack blockchain engineer. Previously software engineer \ + image: '/images/team/amir.jpeg', + linkedIn: 'https://www.linkedin.com/in/abandeali1/', + github: 'https://github.com/abandeali1', + medium: 'https://medium.com/@abandeali1', + }, + { + name: 'Fabio Berger', + title: 'Senior Engineer', + description: `Full-stack blockchain engineer. Previously software engineer \ at Airtable and founder of WealthLift. Computer science at Duke.`, - image: '/images/team/fabio.jpg', - linkedIn: 'https://www.linkedin.com/in/fabio-berger-03ab261a/', - github: 'https://github.com/fabioberger', - medium: 'https://medium.com/@fabioberger', - }, + image: '/images/team/fabio.jpg', + linkedIn: 'https://www.linkedin.com/in/fabio-berger-03ab261a/', + github: 'https://github.com/fabioberger', + medium: 'https://medium.com/@fabioberger', + }, ]; const teamRow2: ProfileInfo[] = [ - { - name: 'Alex Xu', - title: 'Director of Operations', - description: `Strategy and operations. Previously digital marketing at Google \ + { + name: 'Alex Xu', + title: 'Director of Operations', + description: `Strategy and operations. Previously digital marketing at Google \ and vendor management at Amazon. Economics at UC San Diego.`, - image: '/images/team/alex.jpg', - linkedIn: 'https://www.linkedin.com/in/alex-xu/', - github: '', - medium: 'https://medium.com/@aqxu', - }, - { - name: 'Leonid Logvinov', - title: 'Engineer', - description: `Full-stack blockchain engineer. Previously blockchain engineer \ + image: '/images/team/alex.jpg', + linkedIn: 'https://www.linkedin.com/in/alex-xu/', + github: '', + medium: 'https://medium.com/@aqxu', + }, + { + name: 'Leonid Logvinov', + title: 'Engineer', + description: `Full-stack blockchain engineer. Previously blockchain engineer \ at Neufund. Computer science at University of Warsaw.`, - image: '/images/team/leonid.png', - linkedIn: 'https://www.linkedin.com/in/leonidlogvinov/', - github: 'https://github.com/LogvinovLeon', - medium: 'https://medium.com/@Logvinov', - }, - { - name: 'Ben Burns', - title: 'Designer', - description: `Product, motion, and graphic designer. Previously designer \ + image: '/images/team/leonid.png', + linkedIn: 'https://www.linkedin.com/in/leonidlogvinov/', + github: 'https://github.com/LogvinovLeon', + medium: 'https://medium.com/@Logvinov', + }, + { + name: 'Ben Burns', + title: 'Designer', + description: `Product, motion, and graphic designer. Previously designer \ at Airtable and Apple. Digital Design at University of Cincinnati.`, - image: '/images/team/ben.jpg', - linkedIn: 'https://www.linkedin.com/in/ben-burns-30170478/', - github: '', - medium: '', - }, + image: '/images/team/ben.jpg', + linkedIn: 'https://www.linkedin.com/in/ben-burns-30170478/', + github: '', + medium: '', + }, ]; const teamRow3: ProfileInfo[] = [ - { - name: 'Brandon Millman', - title: 'Senior Engineer', - description: `Full-stack engineer. Previously senior software engineer at \ + { + name: 'Brandon Millman', + title: 'Senior Engineer', + description: `Full-stack engineer. Previously senior software engineer at \ Twitter. Electrical and Computer Engineering at Duke.`, - image: '/images/team/brandon.png', - linkedIn: 'https://www.linkedin.com/in/brandon-millman-b093a022/', - github: 'https://github.com/BMillman19', - medium: 'https://medium.com/@bchillman', - }, - { - name: 'Tom Schmidt', - title: 'Product Manager', - description: `Previously engineering at Apple, product management at Facebook and Instagram. Computer Science at Stanford.`, - image: '/images/team/tom.jpg', - linkedIn: 'https://www.linkedin.com/in/tomhschmidt/', - github: 'https://github.com/tomhschmidt', - medium: '', - }, - { - name: 'Jacob Evans', - title: 'Blockchain Engineer', - description: `Previously software engineer at Qantas and RSA Security.`, - image: '/images/team/jacob.jpg', - linkedIn: 'https://www.linkedin.com/in/dekzter/', - github: 'https://github.com/dekz', - medium: '', - }, + image: '/images/team/brandon.png', + linkedIn: 'https://www.linkedin.com/in/brandon-millman-b093a022/', + github: 'https://github.com/BMillman19', + medium: 'https://medium.com/@bchillman', + }, + { + name: 'Tom Schmidt', + title: 'Product Manager', + description: `Previously engineering at Apple, product management at Facebook and Instagram. Computer Science at Stanford.`, + image: '/images/team/tom.jpg', + linkedIn: 'https://www.linkedin.com/in/tomhschmidt/', + github: 'https://github.com/tomhschmidt', + medium: '', + }, + { + name: 'Jacob Evans', + title: 'Blockchain Engineer', + description: `Previously software engineer at Qantas and RSA Security.`, + image: '/images/team/jacob.jpg', + linkedIn: 'https://www.linkedin.com/in/dekzter/', + github: 'https://github.com/dekz', + medium: '', + }, ]; const advisors: ProfileInfo[] = [ - { - name: 'Fred Ehrsam', - description: 'Co-founder of Coinbase. Previously FX trader at Goldman Sachs.', - image: '/images/advisors/fred.jpg', - linkedIn: 'https://www.linkedin.com/in/fredehrsam/', - medium: 'https://medium.com/@FEhrsam', - twitter: 'https://twitter.com/FEhrsam', - }, - { - name: 'Olaf Carlson-Wee', - image: '/images/advisors/olaf.png', - description: 'Founder of Polychain Capital. First hire at Coinbase. Angel investor.', - linkedIn: 'https://www.linkedin.com/in/olafcw/', - angellist: 'https://angel.co/olafcw', - }, - { - name: 'Joey Krug', - description: `Co-CIO at Pantera Capital. Founder of Augur. Thiel 20 Under 20 Fellow.`, - image: '/images/advisors/joey.jpg', - linkedIn: 'https://www.linkedin.com/in/joeykrug/', - github: 'https://github.com/joeykrug', - angellist: 'https://angel.co/joeykrug', - }, - { - name: 'Linda Xie', - description: 'Co-founder of Scalar Capital. Previously PM at Coinbase.', - image: '/images/advisors/linda.jpg', - linkedIn: 'https://www.linkedin.com/in/lindaxie/', - medium: 'https://medium.com/@linda.xie', - twitter: 'https://twitter.com/ljxie', - }, + { + name: 'Fred Ehrsam', + description: 'Co-founder of Coinbase. Previously FX trader at Goldman Sachs.', + image: '/images/advisors/fred.jpg', + linkedIn: 'https://www.linkedin.com/in/fredehrsam/', + medium: 'https://medium.com/@FEhrsam', + twitter: 'https://twitter.com/FEhrsam', + }, + { + name: 'Olaf Carlson-Wee', + image: '/images/advisors/olaf.png', + description: 'Founder of Polychain Capital. First hire at Coinbase. Angel investor.', + linkedIn: 'https://www.linkedin.com/in/olafcw/', + angellist: 'https://angel.co/olafcw', + }, + { + name: 'Joey Krug', + description: `Co-CIO at Pantera Capital. Founder of Augur. Thiel 20 Under 20 Fellow.`, + image: '/images/advisors/joey.jpg', + linkedIn: 'https://www.linkedin.com/in/joeykrug/', + github: 'https://github.com/joeykrug', + angellist: 'https://angel.co/joeykrug', + }, + { + name: 'Linda Xie', + description: 'Co-founder of Scalar Capital. Previously PM at Coinbase.', + image: '/images/advisors/linda.jpg', + linkedIn: 'https://www.linkedin.com/in/lindaxie/', + medium: 'https://medium.com/@linda.xie', + twitter: 'https://twitter.com/ljxie', + }, ]; export interface AboutProps { - source: string; - location: Location; + source: string; + location: Location; } interface AboutState {} const styles: Styles = { - header: { - fontFamily: 'Roboto Mono', - fontSize: 36, - color: 'black', - paddingTop: 110, - }, - weAreHiring: { - fontSize: 30, - color: colors.darkestGrey, - fontFamily: 'Roboto Mono', - letterSpacing: 7.5, - }, + header: { + fontFamily: 'Roboto Mono', + fontSize: 36, + color: 'black', + paddingTop: 110, + }, + weAreHiring: { + fontSize: 30, + color: colors.darkestGrey, + fontFamily: 'Roboto Mono', + letterSpacing: 7.5, + }, }; export class About extends React.Component { - public componentDidMount() { - window.scrollTo(0, 0); - } - public render() { - return ( -
- - -
-
-
About us:
-
- Our team is a diverse and globally distributed group with backgrounds in engineering, - research, business and design. We are passionate about decentralized technology and its - potential to act as an equalizing force in the world. -
-
-
-
{this._renderProfiles(teamRow1)}
-
{this._renderProfiles(teamRow2)}
-
{this._renderProfiles(teamRow3)}
-
-
-
- Advisors: -
-
{this._renderProfiles(advisors)}
-
-
-
- WE'RE HIRING -
-
- We are seeking outstanding candidates to{' '} - - join our team - - . We value passion, diversity and unique perspectives. -
-
-
-
-
- ); - } - private _renderProfiles(profiles: ProfileInfo[]) { - const numIndiv = profiles.length; - const colSize = utils.getColSize(numIndiv); - return _.map(profiles, profile => { - return ( -
- -
- ); - }); - } + public componentDidMount() { + window.scrollTo(0, 0); + } + public render() { + return ( +
+ + +
+
+
About us:
+
+ Our team is a diverse and globally distributed group with backgrounds in engineering, + research, business and design. We are passionate about decentralized technology and its + potential to act as an equalizing force in the world. +
+
+
+
{this._renderProfiles(teamRow1)}
+
{this._renderProfiles(teamRow2)}
+
{this._renderProfiles(teamRow3)}
+
+
+
+ Advisors: +
+
{this._renderProfiles(advisors)}
+
+
+
+ WE'RE HIRING +
+
+ We are seeking outstanding candidates to{' '} + + join our team + + . We value passion, diversity and unique perspectives. +
+
+
+
+
+ ); + } + private _renderProfiles(profiles: ProfileInfo[]) { + const numIndiv = profiles.length; + const colSize = utils.getColSize(numIndiv); + return _.map(profiles, profile => { + return ( +
+ +
+ ); + }); + } } diff --git a/packages/website/ts/pages/about/profile.tsx b/packages/website/ts/pages/about/profile.tsx index f1830851f..18b4e0d5a 100644 --- a/packages/website/ts/pages/about/profile.tsx +++ b/packages/website/ts/pages/about/profile.tsx @@ -5,75 +5,75 @@ import { colors } from 'ts/utils/colors'; const IMAGE_DIMENSION = 149; const styles: Styles = { - subheader: { - textTransform: 'uppercase', - fontSize: 32, - margin: 0, - }, - imageContainer: { - width: IMAGE_DIMENSION, - height: IMAGE_DIMENSION, - boxShadow: 'rgba(0, 0, 0, 0.19) 2px 5px 10px', - }, + subheader: { + textTransform: 'uppercase', + fontSize: 32, + margin: 0, + }, + imageContainer: { + width: IMAGE_DIMENSION, + height: IMAGE_DIMENSION, + boxShadow: 'rgba(0, 0, 0, 0.19) 2px 5px 10px', + }, }; interface ProfileProps { - colSize: number; - profileInfo: ProfileInfo; + colSize: number; + profileInfo: ProfileInfo; } export function Profile(props: ProfileProps) { - return ( -
-
-
- -
-
- {props.profileInfo.name} -
- {!_.isUndefined(props.profileInfo.title) && ( -
- {props.profileInfo.title.toUpperCase()} -
- )} -
- {props.profileInfo.description} -
-
- {renderSocialMediaIcons(props.profileInfo)} -
-
-
- ); + return ( +
+
+
+ +
+
+ {props.profileInfo.name} +
+ {!_.isUndefined(props.profileInfo.title) && ( +
+ {props.profileInfo.title.toUpperCase()} +
+ )} +
+ {props.profileInfo.description} +
+
+ {renderSocialMediaIcons(props.profileInfo)} +
+
+
+ ); } function renderSocialMediaIcons(profileInfo: ProfileInfo) { - const icons = [ - renderSocialMediaIcon('zmdi-github-box', profileInfo.github), - renderSocialMediaIcon('zmdi-linkedin-box', profileInfo.linkedIn), - renderSocialMediaIcon('zmdi-twitter-box', profileInfo.twitter), - ]; - return icons; + const icons = [ + renderSocialMediaIcon('zmdi-github-box', profileInfo.github), + renderSocialMediaIcon('zmdi-linkedin-box', profileInfo.linkedIn), + renderSocialMediaIcon('zmdi-twitter-box', profileInfo.twitter), + ]; + return icons; } function renderSocialMediaIcon(iconName: string, url: string) { - if (_.isEmpty(url)) { - return null; - } + if (_.isEmpty(url)) { + return null; + } - return ( -
- - - -
- ); + return ( +
+ + + +
+ ); } diff --git a/packages/website/ts/pages/documentation/comment.tsx b/packages/website/ts/pages/documentation/comment.tsx index 6be39f586..23cfd96bd 100644 --- a/packages/website/ts/pages/documentation/comment.tsx +++ b/packages/website/ts/pages/documentation/comment.tsx @@ -4,20 +4,20 @@ import * as ReactMarkdown from 'react-markdown'; import { MarkdownCodeBlock } from 'ts/pages/shared/markdown_code_block'; interface CommentProps { - comment: string; - className?: string; + comment: string; + className?: string; } const defaultProps = { - className: '', + className: '', }; export const Comment: React.SFC = (props: CommentProps) => { - return ( -
- -
- ); + return ( +
+ +
+ ); }; Comment.defaultProps = defaultProps; diff --git a/packages/website/ts/pages/documentation/custom_enum.tsx b/packages/website/ts/pages/documentation/custom_enum.tsx index 0006b8d7e..8d50a2f52 100644 --- a/packages/website/ts/pages/documentation/custom_enum.tsx +++ b/packages/website/ts/pages/documentation/custom_enum.tsx @@ -6,27 +6,27 @@ import { utils } from 'ts/utils/utils'; const STRING_ENUM_CODE_PREFIX = ' strEnum('; interface CustomEnumProps { - type: CustomType; + type: CustomType; } // This component renders custom string enums that was a work-around for versions of // TypeScript <2.4.0 that did not support them natively. We keep it around to support // older versions of 0x.js <0.9.0 export function CustomEnum(props: CustomEnumProps) { - const type = props.type; - if (!_.startsWith(type.defaultValue, STRING_ENUM_CODE_PREFIX)) { - utils.consoleLog('We do not yet support `Variable` types that are not strEnums'); - return null; - } - // Remove the prefix and postfix, leaving only the strEnum values without quotes. - const enumValues = type.defaultValue.slice(10, -3).replace(/'/g, ''); - return ( - - {`{`} - {'\t'} - {enumValues} -
- {`}`} -
- ); + const type = props.type; + if (!_.startsWith(type.defaultValue, STRING_ENUM_CODE_PREFIX)) { + utils.consoleLog('We do not yet support `Variable` types that are not strEnums'); + return null; + } + // Remove the prefix and postfix, leaving only the strEnum values without quotes. + const enumValues = type.defaultValue.slice(10, -3).replace(/'/g, ''); + return ( + + {`{`} + {'\t'} + {enumValues} +
+ {`}`} +
+ ); } diff --git a/packages/website/ts/pages/documentation/docs_info.ts b/packages/website/ts/pages/documentation/docs_info.ts index e1080f3a0..4b1ec122a 100644 --- a/packages/website/ts/pages/documentation/docs_info.ts +++ b/packages/website/ts/pages/documentation/docs_info.ts @@ -1,111 +1,111 @@ import compareVersions = require('compare-versions'); import * as _ from 'lodash'; import { - DocAgnosticFormat, - DocsInfoConfig, - DocsMenu, - DoxityDocObj, - MenuSubsectionsBySection, - SectionsMap, - TypeDocNode, + DocAgnosticFormat, + DocsInfoConfig, + DocsMenu, + DoxityDocObj, + MenuSubsectionsBySection, + SectionsMap, + TypeDocNode, } from 'ts/types'; export class DocsInfo { - public displayName: string; - public packageUrl: string; - public subPackageName?: string; - public websitePath: string; - public docsJsonRoot: string; - public menu: DocsMenu; - public sections: SectionsMap; - public sectionNameToMarkdown: { [sectionName: string]: string }; - private _docsInfo: DocsInfoConfig; - constructor(config: DocsInfoConfig) { - this.displayName = config.displayName; - this.packageUrl = config.packageUrl; - this.subPackageName = config.subPackageName; - this.websitePath = config.websitePath; - this.docsJsonRoot = config.docsJsonRoot; - this.sections = config.sections; - this.sectionNameToMarkdown = config.sectionNameToMarkdown; - this._docsInfo = config; - } - public isPublicType(typeName: string): boolean { - if (_.isUndefined(this._docsInfo.publicTypes)) { - return false; - } - const isPublic = _.includes(this._docsInfo.publicTypes, typeName); - return isPublic; - } - public getModulePathsIfExists(sectionName: string): string[] { - const modulePathsIfExists = this._docsInfo.sectionNameToModulePath[sectionName]; - return modulePathsIfExists; - } - public getMenu(selectedVersion?: string): { [section: string]: string[] } { - if (_.isUndefined(selectedVersion) || _.isUndefined(this._docsInfo.menuSubsectionToVersionWhenIntroduced)) { - return this._docsInfo.menu; - } + public displayName: string; + public packageUrl: string; + public subPackageName?: string; + public websitePath: string; + public docsJsonRoot: string; + public menu: DocsMenu; + public sections: SectionsMap; + public sectionNameToMarkdown: { [sectionName: string]: string }; + private _docsInfo: DocsInfoConfig; + constructor(config: DocsInfoConfig) { + this.displayName = config.displayName; + this.packageUrl = config.packageUrl; + this.subPackageName = config.subPackageName; + this.websitePath = config.websitePath; + this.docsJsonRoot = config.docsJsonRoot; + this.sections = config.sections; + this.sectionNameToMarkdown = config.sectionNameToMarkdown; + this._docsInfo = config; + } + public isPublicType(typeName: string): boolean { + if (_.isUndefined(this._docsInfo.publicTypes)) { + return false; + } + const isPublic = _.includes(this._docsInfo.publicTypes, typeName); + return isPublic; + } + public getModulePathsIfExists(sectionName: string): string[] { + const modulePathsIfExists = this._docsInfo.sectionNameToModulePath[sectionName]; + return modulePathsIfExists; + } + public getMenu(selectedVersion?: string): { [section: string]: string[] } { + if (_.isUndefined(selectedVersion) || _.isUndefined(this._docsInfo.menuSubsectionToVersionWhenIntroduced)) { + return this._docsInfo.menu; + } - const finalMenu = _.cloneDeep(this._docsInfo.menu); - if (_.isUndefined(finalMenu.contracts)) { - return finalMenu; - } + const finalMenu = _.cloneDeep(this._docsInfo.menu); + if (_.isUndefined(finalMenu.contracts)) { + return finalMenu; + } - // TODO: refactor to include more sections then simply the `contracts` section - finalMenu.contracts = _.filter(finalMenu.contracts, (contractName: string) => { - const versionIntroducedIfExists = this._docsInfo.menuSubsectionToVersionWhenIntroduced[contractName]; - if (!_.isUndefined(versionIntroducedIfExists)) { - const existsInSelectedVersion = compareVersions(selectedVersion, versionIntroducedIfExists) >= 0; - return existsInSelectedVersion; - } else { - return true; - } - }); - return finalMenu; - } - public getMenuSubsectionsBySection(docAgnosticFormat?: DocAgnosticFormat): MenuSubsectionsBySection { - const menuSubsectionsBySection = {} as MenuSubsectionsBySection; - if (_.isUndefined(docAgnosticFormat)) { - return menuSubsectionsBySection; - } + // TODO: refactor to include more sections then simply the `contracts` section + finalMenu.contracts = _.filter(finalMenu.contracts, (contractName: string) => { + const versionIntroducedIfExists = this._docsInfo.menuSubsectionToVersionWhenIntroduced[contractName]; + if (!_.isUndefined(versionIntroducedIfExists)) { + const existsInSelectedVersion = compareVersions(selectedVersion, versionIntroducedIfExists) >= 0; + return existsInSelectedVersion; + } else { + return true; + } + }); + return finalMenu; + } + public getMenuSubsectionsBySection(docAgnosticFormat?: DocAgnosticFormat): MenuSubsectionsBySection { + const menuSubsectionsBySection = {} as MenuSubsectionsBySection; + if (_.isUndefined(docAgnosticFormat)) { + return menuSubsectionsBySection; + } - const docSections = _.keys(this.sections); - _.each(docSections, sectionName => { - const docSection = docAgnosticFormat[sectionName]; - if (_.isUndefined(docSection)) { - return; // no-op - } + const docSections = _.keys(this.sections); + _.each(docSections, sectionName => { + const docSection = docAgnosticFormat[sectionName]; + if (_.isUndefined(docSection)) { + return; // no-op + } - if (!_.isUndefined(this.sections.types) && sectionName === this.sections.types) { - const sortedTypesNames = _.sortBy(docSection.types, 'name'); - const typeNames = _.map(sortedTypesNames, t => t.name); - menuSubsectionsBySection[sectionName] = typeNames; - } else { - let eventNames: string[] = []; - if (!_.isUndefined(docSection.events)) { - const sortedEventNames = _.sortBy(docSection.events, 'name'); - eventNames = _.map(sortedEventNames, m => m.name); - } - const sortedMethodNames = _.sortBy(docSection.methods, 'name'); - const methodNames = _.map(sortedMethodNames, m => m.name); - menuSubsectionsBySection[sectionName] = [...methodNames, ...eventNames]; - } - }); - return menuSubsectionsBySection; - } - public getTypeDefinitionsByName(docAgnosticFormat: DocAgnosticFormat) { - if (_.isUndefined(this.sections.types)) { - return {}; - } + if (!_.isUndefined(this.sections.types) && sectionName === this.sections.types) { + const sortedTypesNames = _.sortBy(docSection.types, 'name'); + const typeNames = _.map(sortedTypesNames, t => t.name); + menuSubsectionsBySection[sectionName] = typeNames; + } else { + let eventNames: string[] = []; + if (!_.isUndefined(docSection.events)) { + const sortedEventNames = _.sortBy(docSection.events, 'name'); + eventNames = _.map(sortedEventNames, m => m.name); + } + const sortedMethodNames = _.sortBy(docSection.methods, 'name'); + const methodNames = _.map(sortedMethodNames, m => m.name); + menuSubsectionsBySection[sectionName] = [...methodNames, ...eventNames]; + } + }); + return menuSubsectionsBySection; + } + public getTypeDefinitionsByName(docAgnosticFormat: DocAgnosticFormat) { + if (_.isUndefined(this.sections.types)) { + return {}; + } - const typeDocSection = docAgnosticFormat[this.sections.types]; - const typeDefinitionByName = _.keyBy(typeDocSection.types, 'name'); - return typeDefinitionByName; - } - public isVisibleConstructor(sectionName: string): boolean { - return _.includes(this._docsInfo.visibleConstructors, sectionName); - } - public convertToDocAgnosticFormat(docObj: DoxityDocObj | TypeDocNode): DocAgnosticFormat { - return this._docsInfo.convertToDocAgnosticFormatFn(docObj, this); - } + const typeDocSection = docAgnosticFormat[this.sections.types]; + const typeDefinitionByName = _.keyBy(typeDocSection.types, 'name'); + return typeDefinitionByName; + } + public isVisibleConstructor(sectionName: string): boolean { + return _.includes(this._docsInfo.visibleConstructors, sectionName); + } + public convertToDocAgnosticFormat(docObj: DoxityDocObj | TypeDocNode): DocAgnosticFormat { + return this._docsInfo.convertToDocAgnosticFormatFn(docObj, this); + } } diff --git a/packages/website/ts/pages/documentation/documentation.tsx b/packages/website/ts/pages/documentation/documentation.tsx index 0fc41d775..2315847ad 100644 --- a/packages/website/ts/pages/documentation/documentation.tsx +++ b/packages/website/ts/pages/documentation/documentation.tsx @@ -19,17 +19,17 @@ import { NestedSidebarMenu } from 'ts/pages/shared/nested_sidebar_menu'; import { SectionHeader } from 'ts/pages/shared/section_header'; import { Dispatcher } from 'ts/redux/dispatcher'; import { - AddressByContractName, - DocAgnosticFormat, - DoxityDocObj, - EtherscanLinkSuffixes, - Event, - Networks, - Property, - SolidityMethod, - Styles, - TypeDefinitionByName, - TypescriptMethod, + AddressByContractName, + DocAgnosticFormat, + DoxityDocObj, + EtherscanLinkSuffixes, + Event, + Networks, + Property, + SolidityMethod, + Styles, + TypeDefinitionByName, + TypescriptMethod, } from 'ts/types'; import { colors } from 'ts/utils/colors'; import { configs } from 'ts/utils/configs'; @@ -40,340 +40,340 @@ import { utils } from 'ts/utils/utils'; const SCROLL_TOP_ID = 'docsScrollTop'; const networkNameToColor: { [network: string]: string } = { - [Networks.kovan]: colors.purple, - [Networks.ropsten]: colors.red, - [Networks.mainnet]: colors.turquois, + [Networks.kovan]: colors.purple, + [Networks.ropsten]: colors.red, + [Networks.mainnet]: colors.turquois, }; export interface DocumentationAllProps { - source: string; - location: Location; - dispatcher: Dispatcher; - docsVersion: string; - availableDocVersions: string[]; - docsInfo: DocsInfo; + source: string; + location: Location; + dispatcher: Dispatcher; + docsVersion: string; + availableDocVersions: string[]; + docsInfo: DocsInfo; } interface DocumentationState { - docAgnosticFormat?: DocAgnosticFormat; + docAgnosticFormat?: DocAgnosticFormat; } const styles: Styles = { - mainContainers: { - position: 'absolute', - top: 1, - left: 0, - bottom: 0, - right: 0, - overflowZ: 'hidden', - overflowY: 'scroll', - minHeight: 'calc(100vh - 1px)', - WebkitOverflowScrolling: 'touch', - }, - menuContainer: { - borderColor: colors.grey300, - maxWidth: 330, - marginLeft: 20, - }, + mainContainers: { + position: 'absolute', + top: 1, + left: 0, + bottom: 0, + right: 0, + overflowZ: 'hidden', + overflowY: 'scroll', + minHeight: 'calc(100vh - 1px)', + WebkitOverflowScrolling: 'touch', + }, + menuContainer: { + borderColor: colors.grey300, + maxWidth: 330, + marginLeft: 20, + }, }; export class Documentation extends React.Component { - constructor(props: DocumentationAllProps) { - super(props); - this.state = { - docAgnosticFormat: undefined, - }; - } - public componentWillMount() { - const pathName = this.props.location.pathname; - const lastSegment = pathName.substr(pathName.lastIndexOf('/') + 1); - const versions = findVersions(lastSegment); - const preferredVersionIfExists = versions.length > 0 ? versions[0] : undefined; - // tslint:disable-next-line:no-floating-promises - this._fetchJSONDocsFireAndForgetAsync(preferredVersionIfExists); - } - public render() { - const menuSubsectionsBySection = _.isUndefined(this.state.docAgnosticFormat) - ? {} - : this.props.docsInfo.getMenuSubsectionsBySection(this.state.docAgnosticFormat); - return ( -
- - - {_.isUndefined(this.state.docAgnosticFormat) ? ( -
-
-
- -
-
- Loading documentation... -
-
-
- ) : ( -
-
-
- -
-
-
-
-
-

- - {this.props.docsInfo.displayName} - -

- {this._renderDocumentation()} -
-
-
- )} -
- ); - } - private _renderDocumentation(): React.ReactNode { - const subMenus = _.values(this.props.docsInfo.getMenu()); - const orderedSectionNames = _.flatten(subMenus); + constructor(props: DocumentationAllProps) { + super(props); + this.state = { + docAgnosticFormat: undefined, + }; + } + public componentWillMount() { + const pathName = this.props.location.pathname; + const lastSegment = pathName.substr(pathName.lastIndexOf('/') + 1); + const versions = findVersions(lastSegment); + const preferredVersionIfExists = versions.length > 0 ? versions[0] : undefined; + // tslint:disable-next-line:no-floating-promises + this._fetchJSONDocsFireAndForgetAsync(preferredVersionIfExists); + } + public render() { + const menuSubsectionsBySection = _.isUndefined(this.state.docAgnosticFormat) + ? {} + : this.props.docsInfo.getMenuSubsectionsBySection(this.state.docAgnosticFormat); + return ( +
+ + + {_.isUndefined(this.state.docAgnosticFormat) ? ( +
+
+
+ +
+
+ Loading documentation... +
+
+
+ ) : ( +
+
+
+ +
+
+
+
+
+

+ + {this.props.docsInfo.displayName} + +

+ {this._renderDocumentation()} +
+
+
+ )} +
+ ); + } + private _renderDocumentation(): React.ReactNode { + const subMenus = _.values(this.props.docsInfo.getMenu()); + const orderedSectionNames = _.flatten(subMenus); - const typeDefinitionByName = this.props.docsInfo.getTypeDefinitionsByName(this.state.docAgnosticFormat); - const renderedSections = _.map(orderedSectionNames, this._renderSection.bind(this, typeDefinitionByName)); + const typeDefinitionByName = this.props.docsInfo.getTypeDefinitionsByName(this.state.docAgnosticFormat); + const renderedSections = _.map(orderedSectionNames, this._renderSection.bind(this, typeDefinitionByName)); - return renderedSections; - } - private _renderSection(typeDefinitionByName: TypeDefinitionByName, sectionName: string): React.ReactNode { - const markdownFileIfExists = this.props.docsInfo.sectionNameToMarkdown[sectionName]; - if (!_.isUndefined(markdownFileIfExists)) { - return ( - - ); - } + return renderedSections; + } + private _renderSection(typeDefinitionByName: TypeDefinitionByName, sectionName: string): React.ReactNode { + const markdownFileIfExists = this.props.docsInfo.sectionNameToMarkdown[sectionName]; + if (!_.isUndefined(markdownFileIfExists)) { + return ( + + ); + } - const docSection = this.state.docAgnosticFormat[sectionName]; - if (_.isUndefined(docSection)) { - return null; - } + const docSection = this.state.docAgnosticFormat[sectionName]; + if (_.isUndefined(docSection)) { + return null; + } - const sortedTypes = _.sortBy(docSection.types, 'name'); - const typeDefs = _.map(sortedTypes, customType => { - return ( - - ); - }); + const sortedTypes = _.sortBy(docSection.types, 'name'); + const typeDefs = _.map(sortedTypes, customType => { + return ( + + ); + }); - const sortedProperties = _.sortBy(docSection.properties, 'name'); - const propertyDefs = _.map(sortedProperties, this._renderProperty.bind(this, sectionName)); + const sortedProperties = _.sortBy(docSection.properties, 'name'); + const propertyDefs = _.map(sortedProperties, this._renderProperty.bind(this, sectionName)); - const sortedMethods = _.sortBy(docSection.methods, 'name'); - const methodDefs = _.map(sortedMethods, method => { - const isConstructor = false; - return this._renderMethodBlocks(method, sectionName, isConstructor, typeDefinitionByName); - }); + const sortedMethods = _.sortBy(docSection.methods, 'name'); + const methodDefs = _.map(sortedMethods, method => { + const isConstructor = false; + return this._renderMethodBlocks(method, sectionName, isConstructor, typeDefinitionByName); + }); - const sortedEvents = _.sortBy(docSection.events, 'name'); - const eventDefs = _.map(sortedEvents, (event: Event, i: number) => { - return ( - - ); - }); - return ( -
-
-
- -
- {this._renderNetworkBadgesIfExists(sectionName)} -
- {docSection.comment && } - {docSection.constructors.length > 0 && - this.props.docsInfo.isVisibleConstructor(sectionName) && ( -
-

Constructor

- {this._renderConstructors(docSection.constructors, sectionName, typeDefinitionByName)} -
- )} - {docSection.properties.length > 0 && ( -
-

Properties

-
{propertyDefs}
-
- )} - {docSection.methods.length > 0 && ( -
-

Methods

-
{methodDefs}
-
- )} - {!_.isUndefined(docSection.events) && - docSection.events.length > 0 && ( -
-

Events

-
{eventDefs}
-
- )} - {!_.isUndefined(typeDefs) && - typeDefs.length > 0 && ( -
-
{typeDefs}
-
- )} -
- ); - } - private _renderNetworkBadgesIfExists(sectionName: string) { - const networkToAddressByContractName = configs.CONTRACT_ADDRESS[this.props.docsVersion]; - const badges = _.map( - networkToAddressByContractName, - (addressByContractName: AddressByContractName, networkName: string) => { - const contractAddress = addressByContractName[sectionName]; - if (_.isUndefined(contractAddress)) { - return null; - } - const linkIfExists = utils.getEtherScanLinkIfExists( - contractAddress, - constants.NETWORK_ID_BY_NAME[networkName], - EtherscanLinkSuffixes.Address, - ); - return ( - - - - ); - }, - ); - return badges; - } - private _renderConstructors( - constructors: SolidityMethod[] | TypescriptMethod[], - sectionName: string, - typeDefinitionByName: TypeDefinitionByName, - ): React.ReactNode { - const constructorDefs = _.map(constructors, constructor => { - return this._renderMethodBlocks(constructor, sectionName, constructor.isConstructor, typeDefinitionByName); - }); - return
{constructorDefs}
; - } - private _renderProperty(sectionName: string, property: Property): React.ReactNode { - return ( -
- - {property.name}: - - - {property.source && ( - - )} - {property.comment && } -
- ); - } - private _renderMethodBlocks( - method: SolidityMethod | TypescriptMethod, - sectionName: string, - isConstructor: boolean, - typeDefinitionByName: TypeDefinitionByName, - ): React.ReactNode { - return ( - - ); - } - private _scrollToHash(): void { - const hashWithPrefix = this.props.location.hash; - let hash = hashWithPrefix.slice(1); - if (_.isEmpty(hash)) { - hash = SCROLL_TOP_ID; // scroll to the top - } + const sortedEvents = _.sortBy(docSection.events, 'name'); + const eventDefs = _.map(sortedEvents, (event: Event, i: number) => { + return ( + + ); + }); + return ( +
+
+
+ +
+ {this._renderNetworkBadgesIfExists(sectionName)} +
+ {docSection.comment && } + {docSection.constructors.length > 0 && + this.props.docsInfo.isVisibleConstructor(sectionName) && ( +
+

Constructor

+ {this._renderConstructors(docSection.constructors, sectionName, typeDefinitionByName)} +
+ )} + {docSection.properties.length > 0 && ( +
+

Properties

+
{propertyDefs}
+
+ )} + {docSection.methods.length > 0 && ( +
+

Methods

+
{methodDefs}
+
+ )} + {!_.isUndefined(docSection.events) && + docSection.events.length > 0 && ( +
+

Events

+
{eventDefs}
+
+ )} + {!_.isUndefined(typeDefs) && + typeDefs.length > 0 && ( +
+
{typeDefs}
+
+ )} +
+ ); + } + private _renderNetworkBadgesIfExists(sectionName: string) { + const networkToAddressByContractName = configs.CONTRACT_ADDRESS[this.props.docsVersion]; + const badges = _.map( + networkToAddressByContractName, + (addressByContractName: AddressByContractName, networkName: string) => { + const contractAddress = addressByContractName[sectionName]; + if (_.isUndefined(contractAddress)) { + return null; + } + const linkIfExists = utils.getEtherScanLinkIfExists( + contractAddress, + constants.NETWORK_ID_BY_NAME[networkName], + EtherscanLinkSuffixes.Address, + ); + return ( + + + + ); + }, + ); + return badges; + } + private _renderConstructors( + constructors: SolidityMethod[] | TypescriptMethod[], + sectionName: string, + typeDefinitionByName: TypeDefinitionByName, + ): React.ReactNode { + const constructorDefs = _.map(constructors, constructor => { + return this._renderMethodBlocks(constructor, sectionName, constructor.isConstructor, typeDefinitionByName); + }); + return
{constructorDefs}
; + } + private _renderProperty(sectionName: string, property: Property): React.ReactNode { + return ( +
+ + {property.name}: + + + {property.source && ( + + )} + {property.comment && } +
+ ); + } + private _renderMethodBlocks( + method: SolidityMethod | TypescriptMethod, + sectionName: string, + isConstructor: boolean, + typeDefinitionByName: TypeDefinitionByName, + ): React.ReactNode { + return ( + + ); + } + private _scrollToHash(): void { + const hashWithPrefix = this.props.location.hash; + let hash = hashWithPrefix.slice(1); + if (_.isEmpty(hash)) { + hash = SCROLL_TOP_ID; // scroll to the top + } - scroller.scrollTo(hash, { - duration: 0, - offset: 0, - containerId: 'documentation', - }); - } - private async _fetchJSONDocsFireAndForgetAsync(preferredVersionIfExists?: string): Promise { - const versionToFileName = await docUtils.getVersionToFileNameAsync(this.props.docsInfo.docsJsonRoot); - const versions = _.keys(versionToFileName); - this.props.dispatcher.updateAvailableDocVersions(versions); - const sortedVersions = semverSort.desc(versions); - const latestVersion = sortedVersions[0]; + scroller.scrollTo(hash, { + duration: 0, + offset: 0, + containerId: 'documentation', + }); + } + private async _fetchJSONDocsFireAndForgetAsync(preferredVersionIfExists?: string): Promise { + const versionToFileName = await docUtils.getVersionToFileNameAsync(this.props.docsInfo.docsJsonRoot); + const versions = _.keys(versionToFileName); + this.props.dispatcher.updateAvailableDocVersions(versions); + const sortedVersions = semverSort.desc(versions); + const latestVersion = sortedVersions[0]; - let versionToFetch = latestVersion; - if (!_.isUndefined(preferredVersionIfExists)) { - const preferredVersionFileNameIfExists = versionToFileName[preferredVersionIfExists]; - if (!_.isUndefined(preferredVersionFileNameIfExists)) { - versionToFetch = preferredVersionIfExists; - } - } - this.props.dispatcher.updateCurrentDocsVersion(versionToFetch); + let versionToFetch = latestVersion; + if (!_.isUndefined(preferredVersionIfExists)) { + const preferredVersionFileNameIfExists = versionToFileName[preferredVersionIfExists]; + if (!_.isUndefined(preferredVersionFileNameIfExists)) { + versionToFetch = preferredVersionIfExists; + } + } + this.props.dispatcher.updateCurrentDocsVersion(versionToFetch); - const versionFileNameToFetch = versionToFileName[versionToFetch]; - const versionDocObj = await docUtils.getJSONDocFileAsync( - versionFileNameToFetch, - this.props.docsInfo.docsJsonRoot, - ); - const docAgnosticFormat = this.props.docsInfo.convertToDocAgnosticFormat(versionDocObj as DoxityDocObj); + const versionFileNameToFetch = versionToFileName[versionToFetch]; + const versionDocObj = await docUtils.getJSONDocFileAsync( + versionFileNameToFetch, + this.props.docsInfo.docsJsonRoot, + ); + const docAgnosticFormat = this.props.docsInfo.convertToDocAgnosticFormat(versionDocObj as DoxityDocObj); - this.setState( - { - docAgnosticFormat, - }, - () => { - this._scrollToHash(); - }, - ); - } + this.setState( + { + docAgnosticFormat, + }, + () => { + this._scrollToHash(); + }, + ); + } } diff --git a/packages/website/ts/pages/documentation/enum.tsx b/packages/website/ts/pages/documentation/enum.tsx index cfcc7f8d7..7dfdee771 100644 --- a/packages/website/ts/pages/documentation/enum.tsx +++ b/packages/website/ts/pages/documentation/enum.tsx @@ -3,20 +3,20 @@ import * as React from 'react'; import { EnumValue } from 'ts/types'; interface EnumProps { - values: EnumValue[]; + values: EnumValue[]; } export function Enum(props: EnumProps) { - const values = _.map(props.values, (value, i) => { - const defaultValueIfAny = !_.isUndefined(value.defaultValue) ? ` = ${value.defaultValue}` : ''; - return `\n\t${value.name}${defaultValueIfAny},`; - }); - return ( - - {`{`} - {values} -
- {`}`} -
- ); + const values = _.map(props.values, (value, i) => { + const defaultValueIfAny = !_.isUndefined(value.defaultValue) ? ` = ${value.defaultValue}` : ''; + return `\n\t${value.name}${defaultValueIfAny},`; + }); + return ( + + {`{`} + {values} +
+ {`}`} +
+ ); } diff --git a/packages/website/ts/pages/documentation/event_definition.tsx b/packages/website/ts/pages/documentation/event_definition.tsx index 9274ae512..0e53e38e7 100644 --- a/packages/website/ts/pages/documentation/event_definition.tsx +++ b/packages/website/ts/pages/documentation/event_definition.tsx @@ -7,76 +7,76 @@ import { Event, EventArg, HeaderSizes } from 'ts/types'; import { colors } from 'ts/utils/colors'; interface EventDefinitionProps { - event: Event; - sectionName: string; - docsInfo: DocsInfo; + event: Event; + sectionName: string; + docsInfo: DocsInfo; } interface EventDefinitionState { - shouldShowAnchor: boolean; + shouldShowAnchor: boolean; } export class EventDefinition extends React.Component { - constructor(props: EventDefinitionProps) { - super(props); - this.state = { - shouldShowAnchor: false, - }; - } - public render() { - const event = this.props.event; - return ( -
- -
-
-						{this._renderEventCode()}
-					
-
-
- ); - } - private _renderEventCode() { - const indexed = indexed; - const eventArgs = _.map(this.props.event.eventArgs, (eventArg: EventArg) => { - const type = ( - - ); - return ( - - {eventArg.name} - {eventArg.isIndexed ? indexed : ''}: {type}, - - ); - }); - const argList = _.reduce(eventArgs, (prev: React.ReactNode, curr: React.ReactNode) => { - return [prev, '\n\t', curr]; - }); - return ( - - {`{`} -
- {'\t'} - {argList} -
- {`}`} -
- ); - } - private _setAnchorVisibility(shouldShowAnchor: boolean) { - this.setState({ - shouldShowAnchor, - }); - } + constructor(props: EventDefinitionProps) { + super(props); + this.state = { + shouldShowAnchor: false, + }; + } + public render() { + const event = this.props.event; + return ( +
+ +
+
+                        {this._renderEventCode()}
+                    
+
+
+ ); + } + private _renderEventCode() { + const indexed = indexed; + const eventArgs = _.map(this.props.event.eventArgs, (eventArg: EventArg) => { + const type = ( + + ); + return ( + + {eventArg.name} + {eventArg.isIndexed ? indexed : ''}: {type}, + + ); + }); + const argList = _.reduce(eventArgs, (prev: React.ReactNode, curr: React.ReactNode) => { + return [prev, '\n\t', curr]; + }); + return ( + + {`{`} +
+ {'\t'} + {argList} +
+ {`}`} +
+ ); + } + private _setAnchorVisibility(shouldShowAnchor: boolean) { + this.setState({ + shouldShowAnchor, + }); + } } diff --git a/packages/website/ts/pages/documentation/interface.tsx b/packages/website/ts/pages/documentation/interface.tsx index ee07a2c50..16a772125 100644 --- a/packages/website/ts/pages/documentation/interface.tsx +++ b/packages/website/ts/pages/documentation/interface.tsx @@ -6,56 +6,56 @@ import { Type } from 'ts/pages/documentation/type'; import { CustomType, TypeDocTypes } from 'ts/types'; interface InterfaceProps { - type: CustomType; - sectionName: string; - docsInfo: DocsInfo; + type: CustomType; + sectionName: string; + docsInfo: DocsInfo; } export function Interface(props: InterfaceProps) { - const type = props.type; - const properties = _.map(type.children, property => { - return ( - - {property.name}:{' '} - {property.type.typeDocType !== TypeDocTypes.Reflection ? ( - - ) : ( - - )}, - - ); - }); - const hasIndexSignature = !_.isUndefined(type.indexSignature); - if (hasIndexSignature) { - const is = type.indexSignature; - const param = ( - - {is.keyName}: - - ); - properties.push( - - [{param}]: {is.valueName}, - , - ); - } - const propertyList = _.reduce(properties, (prev: React.ReactNode, curr: React.ReactNode) => { - return [prev, '\n\t', curr]; - }); - return ( - - {`{`} -
- {'\t'} - {propertyList} -
- {`}`} -
- ); + const type = props.type; + const properties = _.map(type.children, property => { + return ( + + {property.name}:{' '} + {property.type.typeDocType !== TypeDocTypes.Reflection ? ( + + ) : ( + + )}, + + ); + }); + const hasIndexSignature = !_.isUndefined(type.indexSignature); + if (hasIndexSignature) { + const is = type.indexSignature; + const param = ( + + {is.keyName}: + + ); + properties.push( + + [{param}]: {is.valueName}, + , + ); + } + const propertyList = _.reduce(properties, (prev: React.ReactNode, curr: React.ReactNode) => { + return [prev, '\n\t', curr]; + }); + return ( + + {`{`} +
+ {'\t'} + {propertyList} +
+ {`}`} +
+ ); } diff --git a/packages/website/ts/pages/documentation/method_block.tsx b/packages/website/ts/pages/documentation/method_block.tsx index fb03cf5be..dfde5931b 100644 --- a/packages/website/ts/pages/documentation/method_block.tsx +++ b/packages/website/ts/pages/documentation/method_block.tsx @@ -10,133 +10,133 @@ import { colors } from 'ts/utils/colors'; import { typeDocUtils } from 'ts/utils/typedoc_utils'; interface MethodBlockProps { - method: SolidityMethod | TypescriptMethod; - sectionName: string; - libraryVersion: string; - typeDefinitionByName: TypeDefinitionByName; - docsInfo: DocsInfo; + method: SolidityMethod | TypescriptMethod; + sectionName: string; + libraryVersion: string; + typeDefinitionByName: TypeDefinitionByName; + docsInfo: DocsInfo; } interface MethodBlockState { - shouldShowAnchor: boolean; + shouldShowAnchor: boolean; } const styles: Styles = { - chip: { - fontSize: 13, - backgroundColor: colors.lightBlueA700, - color: colors.white, - height: 11, - borderRadius: 14, - marginTop: 19, - lineHeight: 0.8, - }, + chip: { + fontSize: 13, + backgroundColor: colors.lightBlueA700, + color: colors.white, + height: 11, + borderRadius: 14, + marginTop: 19, + lineHeight: 0.8, + }, }; export class MethodBlock extends React.Component { - constructor(props: MethodBlockProps) { - super(props); - this.state = { - shouldShowAnchor: false, - }; - } - public render() { - const method = this.props.method; - if (typeDocUtils.isPrivateOrProtectedProperty(method.name)) { - return null; - } + constructor(props: MethodBlockProps) { + super(props); + this.state = { + shouldShowAnchor: false, + }; + } + public render() { + const method = this.props.method; + if (typeDocUtils.isPrivateOrProtectedProperty(method.name)) { + return null; + } - return ( -
- {!method.isConstructor && ( -
- {(method as TypescriptMethod).isStatic && this._renderChip('Static')} - {(method as SolidityMethod).isConstant && this._renderChip('Constant')} - {(method as SolidityMethod).isPayable && this._renderChip('Payable')} - -
- )} - - - - {(method as TypescriptMethod).source && ( - - )} - {method.comment && } - {method.parameters && - !_.isEmpty(method.parameters) && ( -
-

- ARGUMENTS -

- {this._renderParameterDescriptions(method.parameters)} -
- )} - {method.returnComment && ( -
-

- RETURNS -

- -
- )} -
- ); - } - private _renderChip(text: string) { - return ( -
- {text} -
- ); - } - private _renderParameterDescriptions(parameters: Parameter[]) { - const descriptions = _.map(parameters, parameter => { - const isOptional = parameter.isOptional; - return ( -
-
-
{parameter.name}
-
- {isOptional && 'optional'} -
-
-
- {parameter.comment && } -
-
- ); - }); - return descriptions; - } - private _setAnchorVisibility(shouldShowAnchor: boolean) { - this.setState({ - shouldShowAnchor, - }); - } + return ( +
+ {!method.isConstructor && ( +
+ {(method as TypescriptMethod).isStatic && this._renderChip('Static')} + {(method as SolidityMethod).isConstant && this._renderChip('Constant')} + {(method as SolidityMethod).isPayable && this._renderChip('Payable')} + +
+ )} + + + + {(method as TypescriptMethod).source && ( + + )} + {method.comment && } + {method.parameters && + !_.isEmpty(method.parameters) && ( +
+

+ ARGUMENTS +

+ {this._renderParameterDescriptions(method.parameters)} +
+ )} + {method.returnComment && ( +
+

+ RETURNS +

+ +
+ )} +
+ ); + } + private _renderChip(text: string) { + return ( +
+ {text} +
+ ); + } + private _renderParameterDescriptions(parameters: Parameter[]) { + const descriptions = _.map(parameters, parameter => { + const isOptional = parameter.isOptional; + return ( +
+
+
{parameter.name}
+
+ {isOptional && 'optional'} +
+
+
+ {parameter.comment && } +
+
+ ); + }); + return descriptions; + } + private _setAnchorVisibility(shouldShowAnchor: boolean) { + this.setState({ + shouldShowAnchor, + }); + } } diff --git a/packages/website/ts/pages/documentation/method_signature.tsx b/packages/website/ts/pages/documentation/method_signature.tsx index 7c6bf96d2..041dcd093 100644 --- a/packages/website/ts/pages/documentation/method_signature.tsx +++ b/packages/website/ts/pages/documentation/method_signature.tsx @@ -6,94 +6,94 @@ import { Parameter, SolidityMethod, TypeDefinitionByName, TypescriptMethod } fro import { constants } from 'ts/utils/constants'; interface MethodSignatureProps { - method: TypescriptMethod | SolidityMethod; - sectionName: string; - shouldHideMethodName?: boolean; - shouldUseArrowSyntax?: boolean; - typeDefinitionByName?: TypeDefinitionByName; - docsInfo: DocsInfo; + method: TypescriptMethod | SolidityMethod; + sectionName: string; + shouldHideMethodName?: boolean; + shouldUseArrowSyntax?: boolean; + typeDefinitionByName?: TypeDefinitionByName; + docsInfo: DocsInfo; } const defaultProps = { - shouldHideMethodName: false, - shouldUseArrowSyntax: false, + shouldHideMethodName: false, + shouldUseArrowSyntax: false, }; export const MethodSignature: React.SFC = (props: MethodSignatureProps) => { - const sectionName = constants.TYPES_SECTION_NAME; - const parameters = renderParameters(props.method, props.docsInfo, sectionName, props.typeDefinitionByName); - const paramString = _.reduce(parameters, (prev: React.ReactNode, curr: React.ReactNode) => { - return [prev, ', ', curr]; - }); - const methodName = props.shouldHideMethodName ? '' : props.method.name; - const typeParameterIfExists = _.isUndefined((props.method as TypescriptMethod).typeParameter) - ? undefined - : renderTypeParameter(props.method, props.docsInfo, sectionName, props.typeDefinitionByName); - return ( - - {props.method.callPath} - {methodName} - {typeParameterIfExists}({paramString}) - {props.shouldUseArrowSyntax ? ' => ' : ': '}{' '} - {props.method.returnType && ( - - )} - - ); + const sectionName = constants.TYPES_SECTION_NAME; + const parameters = renderParameters(props.method, props.docsInfo, sectionName, props.typeDefinitionByName); + const paramString = _.reduce(parameters, (prev: React.ReactNode, curr: React.ReactNode) => { + return [prev, ', ', curr]; + }); + const methodName = props.shouldHideMethodName ? '' : props.method.name; + const typeParameterIfExists = _.isUndefined((props.method as TypescriptMethod).typeParameter) + ? undefined + : renderTypeParameter(props.method, props.docsInfo, sectionName, props.typeDefinitionByName); + return ( + + {props.method.callPath} + {methodName} + {typeParameterIfExists}({paramString}) + {props.shouldUseArrowSyntax ? ' => ' : ': '}{' '} + {props.method.returnType && ( + + )} + + ); }; MethodSignature.defaultProps = defaultProps; function renderParameters( - method: TypescriptMethod | SolidityMethod, - docsInfo: DocsInfo, - sectionName: string, - typeDefinitionByName?: TypeDefinitionByName, + method: TypescriptMethod | SolidityMethod, + docsInfo: DocsInfo, + sectionName: string, + typeDefinitionByName?: TypeDefinitionByName, ) { - const parameters = method.parameters; - const params = _.map(parameters, (p: Parameter) => { - const isOptional = p.isOptional; - const type = ( - - ); - return ( - - {p.name} - {isOptional && '?'}: {type} - - ); - }); - return params; + const parameters = method.parameters; + const params = _.map(parameters, (p: Parameter) => { + const isOptional = p.isOptional; + const type = ( + + ); + return ( + + {p.name} + {isOptional && '?'}: {type} + + ); + }); + return params; } function renderTypeParameter( - method: TypescriptMethod, - docsInfo: DocsInfo, - sectionName: string, - typeDefinitionByName?: TypeDefinitionByName, + method: TypescriptMethod, + docsInfo: DocsInfo, + sectionName: string, + typeDefinitionByName?: TypeDefinitionByName, ) { - const typeParameter = method.typeParameter; - const typeParam = ( - - {`<${typeParameter.name} extends `} - - {`>`} - - ); - return typeParam; + const typeParameter = method.typeParameter; + const typeParam = ( + + {`<${typeParameter.name} extends `} + + {`>`} + + ); + return typeParam; } diff --git a/packages/website/ts/pages/documentation/source_link.tsx b/packages/website/ts/pages/documentation/source_link.tsx index 32126b7da..6588ee39e 100644 --- a/packages/website/ts/pages/documentation/source_link.tsx +++ b/packages/website/ts/pages/documentation/source_link.tsx @@ -4,28 +4,28 @@ import { Source } from 'ts/types'; import { colors } from 'ts/utils/colors'; interface SourceLinkProps { - source: Source; - baseUrl: string; - version: string; - subPackageName: string; + source: Source; + baseUrl: string; + version: string; + subPackageName: string; } const packagesWithNamespace = ['connect']; export function SourceLink(props: SourceLinkProps) { - const src = props.source; - const url = props.baseUrl; - const pkg = props.subPackageName; - let tagPrefix = pkg; - if (_.includes(packagesWithNamespace, pkg)) { - tagPrefix = `@0xproject/${pkg}`; - } - const sourceCodeUrl = `${url}/blob/${tagPrefix}%40${props.version}/packages/${pkg}/${src.fileName}#L${src.line}`; - return ( - - ); + const src = props.source; + const url = props.baseUrl; + const pkg = props.subPackageName; + let tagPrefix = pkg; + if (_.includes(packagesWithNamespace, pkg)) { + tagPrefix = `@0xproject/${pkg}`; + } + const sourceCodeUrl = `${url}/blob/${tagPrefix}%40${props.version}/packages/${pkg}/${src.fileName}#L${src.line}`; + return ( + + ); } diff --git a/packages/website/ts/pages/documentation/type.tsx b/packages/website/ts/pages/documentation/type.tsx index 9a2696e22..e989e7129 100644 --- a/packages/website/ts/pages/documentation/type.tsx +++ b/packages/website/ts/pages/documentation/type.tsx @@ -11,202 +11,202 @@ import { utils } from 'ts/utils/utils'; // Some types reference other libraries. For these types, we want to link the user to the relevant documentation. const typeToUrl: { [typeName: string]: string } = { - Web3: constants.URL_WEB3_DOCS, - Provider: constants.URL_WEB3_PROVIDER_DOCS, - BigNumber: constants.URL_BIGNUMBERJS_GITHUB, - DecodedLogEntryEvent: constants.URL_WEB3_DECODED_LOG_ENTRY_EVENT, - LogEntryEvent: constants.URL_WEB3_LOG_ENTRY_EVENT, + Web3: constants.URL_WEB3_DOCS, + Provider: constants.URL_WEB3_PROVIDER_DOCS, + BigNumber: constants.URL_BIGNUMBERJS_GITHUB, + DecodedLogEntryEvent: constants.URL_WEB3_DECODED_LOG_ENTRY_EVENT, + LogEntryEvent: constants.URL_WEB3_LOG_ENTRY_EVENT, }; const typePrefix: { [typeName: string]: string } = { - Provider: 'Web3', - DecodedLogEntryEvent: 'Web3', - LogEntryEvent: 'Web3', + Provider: 'Web3', + DecodedLogEntryEvent: 'Web3', + LogEntryEvent: 'Web3', }; const typeToSection: { [typeName: string]: string } = { - ExchangeWrapper: 'exchange', - TokenWrapper: 'token', - TokenRegistryWrapper: 'tokenRegistry', - EtherTokenWrapper: 'etherToken', - ProxyWrapper: 'proxy', - TokenTransferProxyWrapper: 'proxy', - OrderStateWatcher: 'orderWatcher', + ExchangeWrapper: 'exchange', + TokenWrapper: 'token', + TokenRegistryWrapper: 'tokenRegistry', + EtherTokenWrapper: 'etherToken', + ProxyWrapper: 'proxy', + TokenTransferProxyWrapper: 'proxy', + OrderStateWatcher: 'orderWatcher', }; interface TypeProps { - type: TypeDef; - docsInfo: DocsInfo; - sectionName: string; - typeDefinitionByName?: TypeDefinitionByName; + type: TypeDef; + docsInfo: DocsInfo; + sectionName: string; + typeDefinitionByName?: TypeDefinitionByName; } // The return type needs to be `any` here so that we can recursively define components within // components (e.g when rendering the union type). export function Type(props: TypeProps): any { - const type = props.type; - const isReference = type.typeDocType === TypeDocTypes.Reference; - const isArray = type.typeDocType === TypeDocTypes.Array; - let typeNameColor = 'inherit'; - let typeName: string | React.ReactNode; - let typeArgs: React.ReactNode[] = []; - switch (type.typeDocType) { - case TypeDocTypes.Intrinsic: - case TypeDocTypes.Unknown: - typeName = type.name; - typeNameColor = colors.orange; - break; + const type = props.type; + const isReference = type.typeDocType === TypeDocTypes.Reference; + const isArray = type.typeDocType === TypeDocTypes.Array; + let typeNameColor = 'inherit'; + let typeName: string | React.ReactNode; + let typeArgs: React.ReactNode[] = []; + switch (type.typeDocType) { + case TypeDocTypes.Intrinsic: + case TypeDocTypes.Unknown: + typeName = type.name; + typeNameColor = colors.orange; + break; - case TypeDocTypes.Reference: - typeName = type.name; - typeArgs = _.map(type.typeArguments, (arg: TypeDef) => { - if (arg.typeDocType === TypeDocTypes.Array) { - const key = `type-${arg.elementType.name}-${arg.elementType.typeDocType}`; - return ( - - [] - - ); - } else { - const subType = ( - - ); - return subType; - } - }); - break; + case TypeDocTypes.Reference: + typeName = type.name; + typeArgs = _.map(type.typeArguments, (arg: TypeDef) => { + if (arg.typeDocType === TypeDocTypes.Array) { + const key = `type-${arg.elementType.name}-${arg.elementType.typeDocType}`; + return ( + + [] + + ); + } else { + const subType = ( + + ); + return subType; + } + }); + break; - case TypeDocTypes.StringLiteral: - typeName = `'${type.value}'`; - typeNameColor = colors.green; - break; + case TypeDocTypes.StringLiteral: + typeName = `'${type.value}'`; + typeNameColor = colors.green; + break; - case TypeDocTypes.Array: - typeName = type.elementType.name; - break; + case TypeDocTypes.Array: + typeName = type.elementType.name; + break; - case TypeDocTypes.Union: - const unionTypes = _.map(type.types, t => { - return ( - - ); - }); - typeName = _.reduce(unionTypes, (prev: React.ReactNode, curr: React.ReactNode) => { - return [prev, '|', curr]; - }); - break; + case TypeDocTypes.Union: + const unionTypes = _.map(type.types, t => { + return ( + + ); + }); + typeName = _.reduce(unionTypes, (prev: React.ReactNode, curr: React.ReactNode) => { + return [prev, '|', curr]; + }); + break; - case TypeDocTypes.TypeParameter: - typeName = type.name; - break; + case TypeDocTypes.TypeParameter: + typeName = type.name; + break; - default: - throw utils.spawnSwitchErr('type.typeDocType', type.typeDocType); - } - // HACK: Normalize BigNumber to simply BigNumber. For some reason the type - // name is unpredictably one or the other. - if (typeName === 'BigNumber') { - typeName = 'BigNumber'; - } - const commaSeparatedTypeArgs = _.reduce(typeArgs, (prev: React.ReactNode, curr: React.ReactNode) => { - return [prev, ', ', curr]; - }); + default: + throw utils.spawnSwitchErr('type.typeDocType', type.typeDocType); + } + // HACK: Normalize BigNumber to simply BigNumber. For some reason the type + // name is unpredictably one or the other. + if (typeName === 'BigNumber') { + typeName = 'BigNumber'; + } + const commaSeparatedTypeArgs = _.reduce(typeArgs, (prev: React.ReactNode, curr: React.ReactNode) => { + return [prev, ', ', curr]; + }); - const typeNameUrlIfExists = typeToUrl[typeName as string]; - const typePrefixIfExists = typePrefix[typeName as string]; - const sectionNameIfExists = typeToSection[typeName as string]; - if (!_.isUndefined(typeNameUrlIfExists)) { - typeName = ( - - {!_.isUndefined(typePrefixIfExists) ? `${typePrefixIfExists}.` : ''} - {typeName} - - ); - } else if ( - (isReference || isArray) && - (props.docsInfo.isPublicType(typeName as string) || !_.isUndefined(sectionNameIfExists)) - ) { - const id = Math.random().toString(); - const typeDefinitionAnchorId = _.isUndefined(sectionNameIfExists) - ? `${props.sectionName}-${typeName}` - : sectionNameIfExists; - let typeDefinition; - if (props.typeDefinitionByName) { - typeDefinition = props.typeDefinitionByName[typeName as string]; - } - typeName = ( - - {_.isUndefined(typeDefinition) || utils.isUserOnMobile() ? ( - - {typeName} - - ) : ( - - {typeName} - - - - - )} - - ); - } - return ( - - {typeName} - {isArray && '[]'} - {!_.isEmpty(typeArgs) && ( - - {'<'} - {commaSeparatedTypeArgs} - {'>'} - - )} - - ); + const typeNameUrlIfExists = typeToUrl[typeName as string]; + const typePrefixIfExists = typePrefix[typeName as string]; + const sectionNameIfExists = typeToSection[typeName as string]; + if (!_.isUndefined(typeNameUrlIfExists)) { + typeName = ( + + {!_.isUndefined(typePrefixIfExists) ? `${typePrefixIfExists}.` : ''} + {typeName} + + ); + } else if ( + (isReference || isArray) && + (props.docsInfo.isPublicType(typeName as string) || !_.isUndefined(sectionNameIfExists)) + ) { + const id = Math.random().toString(); + const typeDefinitionAnchorId = _.isUndefined(sectionNameIfExists) + ? `${props.sectionName}-${typeName}` + : sectionNameIfExists; + let typeDefinition; + if (props.typeDefinitionByName) { + typeDefinition = props.typeDefinitionByName[typeName as string]; + } + typeName = ( + + {_.isUndefined(typeDefinition) || utils.isUserOnMobile() ? ( + + {typeName} + + ) : ( + + {typeName} + + + + + )} + + ); + } + return ( + + {typeName} + {isArray && '[]'} + {!_.isEmpty(typeArgs) && ( + + {'<'} + {commaSeparatedTypeArgs} + {'>'} + + )} + + ); } diff --git a/packages/website/ts/pages/documentation/type_definition.tsx b/packages/website/ts/pages/documentation/type_definition.tsx index 356926157..d46eec76c 100644 --- a/packages/website/ts/pages/documentation/type_definition.tsx +++ b/packages/website/ts/pages/documentation/type_definition.tsx @@ -13,113 +13,113 @@ import { colors } from 'ts/utils/colors'; import { utils } from 'ts/utils/utils'; interface TypeDefinitionProps { - sectionName: string; - customType: CustomType; - shouldAddId?: boolean; - docsInfo: DocsInfo; + sectionName: string; + customType: CustomType; + shouldAddId?: boolean; + docsInfo: DocsInfo; } interface TypeDefinitionState { - shouldShowAnchor: boolean; + shouldShowAnchor: boolean; } export class TypeDefinition extends React.Component { - public static defaultProps: Partial = { - shouldAddId: true, - }; - constructor(props: TypeDefinitionProps) { - super(props); - this.state = { - shouldShowAnchor: false, - }; - } - public render() { - const customType = this.props.customType; - if (!this.props.docsInfo.isPublicType(customType.name)) { - return null; // no-op - } + public static defaultProps: Partial = { + shouldAddId: true, + }; + constructor(props: TypeDefinitionProps) { + super(props); + this.state = { + shouldShowAnchor: false, + }; + } + public render() { + const customType = this.props.customType; + if (!this.props.docsInfo.isPublicType(customType.name)) { + return null; // no-op + } - let typePrefix: string; - let codeSnippet: React.ReactNode; - switch (customType.kindString) { - case KindString.Interface: - typePrefix = 'Interface'; - codeSnippet = ( - - ); - break; + let typePrefix: string; + let codeSnippet: React.ReactNode; + switch (customType.kindString) { + case KindString.Interface: + typePrefix = 'Interface'; + codeSnippet = ( + + ); + break; - case KindString.Variable: - typePrefix = 'Enum'; - codeSnippet = ; - break; + case KindString.Variable: + typePrefix = 'Enum'; + codeSnippet = ; + break; - case KindString.Enumeration: - typePrefix = 'Enum'; - const enumValues = _.map(customType.children, (c: CustomTypeChild) => { - return { - name: c.name, - defaultValue: c.defaultValue, - }; - }); - codeSnippet = ; - break; + case KindString.Enumeration: + typePrefix = 'Enum'; + const enumValues = _.map(customType.children, (c: CustomTypeChild) => { + return { + name: c.name, + defaultValue: c.defaultValue, + }; + }); + codeSnippet = ; + break; - case KindString.TypeAlias: - typePrefix = 'Type Alias'; - codeSnippet = ( - - type {customType.name} ={' '} - {customType.type.typeDocType !== TypeDocTypes.Reflection ? ( - - ) : ( - - )} - - ); - break; + case KindString.TypeAlias: + typePrefix = 'Type Alias'; + codeSnippet = ( + + type {customType.name} ={' '} + {customType.type.typeDocType !== TypeDocTypes.Reflection ? ( + + ) : ( + + )} + + ); + break; - default: - throw utils.spawnSwitchErr('type.kindString', customType.kindString); - } + default: + throw utils.spawnSwitchErr('type.kindString', customType.kindString); + } - const typeDefinitionAnchorId = `${this.props.sectionName}-${customType.name}`; - return ( -
- -
-
-						{codeSnippet}
-					
-
- {customType.comment && } -
- ); - } - private _setAnchorVisibility(shouldShowAnchor: boolean) { - this.setState({ - shouldShowAnchor, - }); - } + const typeDefinitionAnchorId = `${this.props.sectionName}-${customType.name}`; + return ( +
+ +
+
+                        {codeSnippet}
+                    
+
+ {customType.comment && } +
+ ); + } + private _setAnchorVisibility(shouldShowAnchor: boolean) { + this.setState({ + shouldShowAnchor, + }); + } } diff --git a/packages/website/ts/pages/faq/faq.tsx b/packages/website/ts/pages/faq/faq.tsx index f437f5a8d..b4b5214a2 100644 --- a/packages/website/ts/pages/faq/faq.tsx +++ b/packages/website/ts/pages/faq/faq.tsx @@ -10,438 +10,438 @@ import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; export interface FAQProps { - source: string; - location: Location; + source: string; + location: Location; } interface FAQState {} const styles: Styles = { - thin: { - fontWeight: 100, - }, + thin: { + fontWeight: 100, + }, }; const sections: FAQSection[] = [ - { - name: '0x Protocol', - questions: [ - { - prompt: 'What is 0x?', - answer: ( -
- At its core, 0x is an open and non-rent seeking protocol that facilitates trustless, low - friction exchange of Ethereum-based assets. Developers can use 0x as a platform to build - exchange applications on top of ( - 0x.js - {' '} - is a Javascript library for interacting with the 0x protocol). For end users, 0x will be the - infrastructure of a wide variety of user-facing applications i.e.{' '} - - 0x Portal - , a decentralized application that facilitates trustless trading of Ethereum-based tokens - between known counterparties. -
- ), - }, - { - prompt: 'What problem does 0x solve?', - answer: ( -
- In the two years since the Ethereum blockchain’s genesis block, numerous decentralized - applications (dApps) have created Ethereum smart contracts for peer-to-peer exchange. Rapid - iteration and a lack of best practices have left the blockchain scattered with proprietary and - application-specific implementations. As a result, end users are exposed to numerous smart - contracts of varying quality and security, with unique configuration processes and learning - curves, all of which implement the same functionality. This approach imposes unnecessary costs - on the network by fragmenting end users according to the particular dApp each user happens to be - using, eliminating valuable network effects around liquidity. 0x is the solution to this problem - by acting as modular, unopinionated building blocks that may be assembled and reconfigured. -
- ), - }, - { - prompt: 'How is 0x different from a centralized exchange like Poloniex or ShapeShift?', - answer: ( -
-
    -
  • 0x is a protocol for exchange, not a user-facing exchange application.
  • -
  • - 0x is decentralized and trustless; there is no central party which can be hacked, run - away with customer funds or be subjected to government regulations. Hacks of Mt. Gox, - Shapeshift and Bitfinex have demonstrated that these types of systemic risks are - palpable. -
  • -
  • - Rather than a proprietary system that exists to extract rent for its owners, 0x is - public infrastructure that is funded by a globally distributed community of - stakeholders. While the protocol is free to use, it enables for-profit user-facing - exchange applications to be built on top of the protocol. -
  • -
-
- ), - }, - { - prompt: 'If 0x protocol is free to use, where do transaction fees come in?', - answer: ( -
- 0x protocol uses off-chain order books to massively reduce friction costs for market makers and - ensure that the blockchain is only used for trade settlement. Hosting and maintaining an - off-chain order book is a service; to incent “Relayers” to provide this service they must be - able to charge transaction fees on trading activity. Relayers are free to set their transaction - fees to any value they desire. We expect Relayers to be highly competitive and transaction fees - to approach an efficient economic equilibrium over time. -
- ), - }, - { - prompt: 'What are the differences between 0x protocol and state channels?', - answer: ( -
-
- Participants in a state channel pass cryptographically signed messages back and forth, - accumulating intermediate state changes without publishing them to the canonical chain until - the channel is closed. State channels are ideal for “bar tab” applications where numerous - intermediate state changes may be accumulated off-chain before being settled by a final - on-chain transaction (i.e. day trading, poker, turn-based games). -
-
    -
  • - While state channels drastically reduce the number of on-chain transactions for specific - use cases, numerous on-chain transactions and a security deposit are required to open - and safely close a state channel making them less efficient than 0x for executing - one-time trades. -
  • -
  • - State channels are isolated from the Ethereum blockchain meaning that they cannot - interact with smart contracts. 0x is designed to be integrated directly into smart - contracts so trades can be executed programmatically in a single line of Solidity code. -
  • -
-
- ), - }, - { - prompt: 'What types of digital assets are supported by 0x?', - answer: ( -
- 0x supports all Ethereum-based assets that adhere to the ERC20 token standard. There are many - ERC20 tokens, worth a combined $2.2B, and more tokens are created each month. We believe that, - by 2020, thousands of assets will be tokenized and moved onto the Ethereum blockchain including - traditional securities such as equities, bonds and derivatives, fiat currencies and scarce - digital goods such as video game items. In the future, cross-blockchain solutions such as{' '} - - Cosmos - {' '} - and{' '} - - Polkadot - {' '} - will allow cryptocurrencies to freely move between blockchains and, naturally, currencies such - as Bitcoin will end up being represented as ERC20 tokens on the Ethereum blockchain. -
- ), - }, - { - prompt: '0x is open source: what prevents someone from forking the protocol?', - answer: ( -
- Ethereum and Bitcoin are both open source protocols. Each protocol has been forked, but the - resulting clone networks have seen little adoption (as measured by transaction count or market - cap). This is because users have little to no incentive to switch over to a clone network if the - original has initial network effects and a talented developer team behind it. An exception is in - the case that a protocol includes a controversial feature such as a method of rent extraction or - a monetary policy that favors one group of users over another (Zcash developer subsidy - for - better or worse - resulted in Zclassic). Perceived inequality can provide a strong enough - incentive that users will fork the original protocol’s codebase and spin up a new network that - eliminates the controversial feature. In the case of 0x, there is no rent extraction and no - users are given special permissions. 0x protocol is upgradable. Cutting-edge technical - capabilities can be integrated into 0x via decentralized governance (see section below), - eliminating incentives to fork off of the original protocol and sacrifice the network effects - surrounding liquidity that result from the shared protocol and settlement layer. -
- ), - }, - ], - }, - { - name: '0x Token (ZRX)', - questions: [ - { - prompt: 'Explain how the 0x protocol token (zrx) works.', - answer: ( -
-
- 0x protocol token (ZRX) is utilized in two ways: 1) to solve the{' '} - - coordination problem - {' '} - and drive network effects around liquidity, creating a feedback loop where early adopters of - the protocol benefit from wider adoption and 2) to be used for decentralized governance over - 0x protocol's update mechanism. In more detail: -
-
    -
  • - ZRX tokens are used by Makers and Takers (market participants that generate and consume - orders, respectively) to pay transaction fees to Relayers (entities that host and - maintain public order books). -
  • -
  • - ZRX tokens are used for decentralized governance over 0x protocol’s update mechanism - which allows its underlying smart contracts to be replaced and improved over time. An - update mechanism is needed because 0x is built upon Ethereum’s rapidly evolving - technology stack, decentralized governance is needed because 0x protocol’s smart - contracts will have access to user funds and numerous dApps will need to plug into 0x - smart contracts. Decentralized governance ensures that this update process is secure and - minimizes disruption to the network. -
  • -
-
- ), - }, - { - prompt: 'Why must transaction fees be denominated in 0x token (ZRX) rather than ETH?', - answer: ( -
- 0x protocol’s decentralized update mechanism is analogous to proof-of-stake. To maximize the - alignment of stakeholder and end user incentives, the staked asset must provide utility within - the protocol. -
- ), - }, - { - prompt: 'How will decentralized governance work?', - answer: ( -
- Decentralized governance is an ongoing focus of research; it will involve token voting with ZRX. - Ultimately the solution will maximize security while also maximizing the protocol’s ability to - absorb new innovations. Until the governance structure is formalized and encoded within 0x DAO, - a multi-sig will be used as a placeholder. -
- ), - }, - ], - }, - { - name: 'ZRX Token Launch and Fund Use', - questions: [ - { - prompt: 'What is the total supply of ZRX tokens?', - answer:
1,000,000,000 ZRX. Fixed supply.
, - }, - { - prompt: 'When is the Token Launch? will there be a pre-sale?', - answer:
The token launch will be on August 15th, 2017. There will not be a pre-sale.
, - }, - { - prompt: 'What will the token launch proceeds be used for?', - answer: ( -
- 100% of the proceeds raised in the token launch will be used to fund the development of free and - open source software, tools and infrastructure that support the protocol and surrounding - ecosystem. Check out our{' '} - - development roadmap - . -
- ), - }, - { - prompt: 'What will be the initial distribution of ZRX tokens?', - answer: ( -
-
- -
-
-
Token Launch (50%)
-
- ZRX is inherently a governance token that plays a critical role in the process of - upgrading 0x protocol. We are fully committed to formulating a functional and - theoretically sound governance model and we plan to dedicate significant resources to - R&D. -
-
-
-
Retained by 0x (15%)
-
- The 0x core development team will be able to sustain itself for approximately five years - using funds raised through the token launch. If 0x protocol proves to be as foundational - a technology as we believe it to be, the retained ZRX tokens will allow the 0x core - development team to sustain operations beyond the first 5 years. -
-
-
-
Developer Fund (15%)
-
- The Developer Fund will be used to make targeted capital injections into high potential - projects and teams that are attempting to grow the 0x ecosystem, strategic partnerships, - hackathon prizes and community development activities. -
-
-
-
Founding Team (10%)
-
- The founding team’s allocation of ZRX will vest over a traditional 4 year vesting - schedule with a one year cliff. We believe this should be standard practice for any team - that is committed to making their project a long term success. -
-
-
-
Early Backers & Advisors (10%)
-
- Our backers and advisors have provided capital, resources and guidance that have allowed - us to fill out our team, setup a robust legal entity and build a fully functional - product before launching a token. As a result, we have a proven track record and can - offer a token that holds genuine utility. -
-
-
- ), - }, - { - prompt: 'Can I mine ZRX tokens?', - answer: ( -
- No, the total supply of ZRX tokens is fixed and there is no continuous issuance model. Users - that facilitate trading over 0x protocol by operating a Relayer earn transaction fees - denominated in ZRX; as more trading activity is generated, more transaction fees are earned. -
- ), - }, - { - prompt: 'Will there be a lockup period for ZRX tokens sold in the token launch?', - answer:
No, ZRX tokens sold in the token launch will immediately be liquid.
, - }, - { - prompt: 'Will there be a lockup period for tokens allocated to the founding team?', - answer: ( -
- Yes. ZRX tokens allocated to founders, advisors and staff members will be released over a 4 year - vesting schedule with a 25% cliff upon completion of the initial token launch and 25% released - each subsequent year in monthly installments. Staff members hired after the token launch will - have a 4 year vesting schedule with a one year cliff. -
- ), - }, - { - prompt: 'Which cryptocurrencies will be accepted in the token launch?', - answer:
ETH.
, - }, - { - prompt: 'When will 0x be live?', - answer: ( -
- An alpha version of 0x has been live on our private test network since January 2017. Version 1.0 - of 0x protocol will be deployed to the canonical Ethereum blockchain after a round of security - audits and prior to the public token launch. 0x will be using the 0x protocol during our token - launch. -
- ), - }, - { - prompt: 'Where can I find a development roadmap?', - answer: ( -
- Check it out{' '} - - here - . -
- ), - }, - ], - }, - { - name: 'Team', - questions: [ - { - prompt: 'Where is 0x based?', - answer:
0x was founded in SF and is driven by a diverse group of contributors.
, - }, - { - prompt: 'How can I get involved?', - answer: ( -
- Join our{' '} - - Rocket.chat - ! As an open source project, 0x will rely on a worldwide community of passionate developers - to contribute proposals, ideas and code. -
- ), - }, - { - prompt: 'Why the name 0x?', - answer: ( -
- 0x is the prefix for hexadecimal numeric constants including Ethereum addresses. In a more - abstract context, as the first open protocol for exchange 0x represents the beginning of the end - for the exchange industry’s rent seeking oligopoly: zero exchange. -
- ), - }, - { - prompt: 'How do you pronounce 0x?', - answer:
We pronounce 0x as “zero-ex,” but you are free to pronounce it however you please.
, - }, - ], - }, + { + name: '0x Protocol', + questions: [ + { + prompt: 'What is 0x?', + answer: ( +
+ At its core, 0x is an open and non-rent seeking protocol that facilitates trustless, low + friction exchange of Ethereum-based assets. Developers can use 0x as a platform to build + exchange applications on top of ( + 0x.js + {' '} + is a Javascript library for interacting with the 0x protocol). For end users, 0x will be the + infrastructure of a wide variety of user-facing applications i.e.{' '} + + 0x Portal + , a decentralized application that facilitates trustless trading of Ethereum-based tokens + between known counterparties. +
+ ), + }, + { + prompt: 'What problem does 0x solve?', + answer: ( +
+ In the two years since the Ethereum blockchain’s genesis block, numerous decentralized + applications (dApps) have created Ethereum smart contracts for peer-to-peer exchange. Rapid + iteration and a lack of best practices have left the blockchain scattered with proprietary and + application-specific implementations. As a result, end users are exposed to numerous smart + contracts of varying quality and security, with unique configuration processes and learning + curves, all of which implement the same functionality. This approach imposes unnecessary costs + on the network by fragmenting end users according to the particular dApp each user happens to be + using, eliminating valuable network effects around liquidity. 0x is the solution to this problem + by acting as modular, unopinionated building blocks that may be assembled and reconfigured. +
+ ), + }, + { + prompt: 'How is 0x different from a centralized exchange like Poloniex or ShapeShift?', + answer: ( +
+
    +
  • 0x is a protocol for exchange, not a user-facing exchange application.
  • +
  • + 0x is decentralized and trustless; there is no central party which can be hacked, run + away with customer funds or be subjected to government regulations. Hacks of Mt. Gox, + Shapeshift and Bitfinex have demonstrated that these types of systemic risks are + palpable. +
  • +
  • + Rather than a proprietary system that exists to extract rent for its owners, 0x is + public infrastructure that is funded by a globally distributed community of + stakeholders. While the protocol is free to use, it enables for-profit user-facing + exchange applications to be built on top of the protocol. +
  • +
+
+ ), + }, + { + prompt: 'If 0x protocol is free to use, where do transaction fees come in?', + answer: ( +
+ 0x protocol uses off-chain order books to massively reduce friction costs for market makers and + ensure that the blockchain is only used for trade settlement. Hosting and maintaining an + off-chain order book is a service; to incent “Relayers” to provide this service they must be + able to charge transaction fees on trading activity. Relayers are free to set their transaction + fees to any value they desire. We expect Relayers to be highly competitive and transaction fees + to approach an efficient economic equilibrium over time. +
+ ), + }, + { + prompt: 'What are the differences between 0x protocol and state channels?', + answer: ( +
+
+ Participants in a state channel pass cryptographically signed messages back and forth, + accumulating intermediate state changes without publishing them to the canonical chain until + the channel is closed. State channels are ideal for “bar tab” applications where numerous + intermediate state changes may be accumulated off-chain before being settled by a final + on-chain transaction (i.e. day trading, poker, turn-based games). +
+
    +
  • + While state channels drastically reduce the number of on-chain transactions for specific + use cases, numerous on-chain transactions and a security deposit are required to open + and safely close a state channel making them less efficient than 0x for executing + one-time trades. +
  • +
  • + State channels are isolated from the Ethereum blockchain meaning that they cannot + interact with smart contracts. 0x is designed to be integrated directly into smart + contracts so trades can be executed programmatically in a single line of Solidity code. +
  • +
+
+ ), + }, + { + prompt: 'What types of digital assets are supported by 0x?', + answer: ( +
+ 0x supports all Ethereum-based assets that adhere to the ERC20 token standard. There are many + ERC20 tokens, worth a combined $2.2B, and more tokens are created each month. We believe that, + by 2020, thousands of assets will be tokenized and moved onto the Ethereum blockchain including + traditional securities such as equities, bonds and derivatives, fiat currencies and scarce + digital goods such as video game items. In the future, cross-blockchain solutions such as{' '} + + Cosmos + {' '} + and{' '} + + Polkadot + {' '} + will allow cryptocurrencies to freely move between blockchains and, naturally, currencies such + as Bitcoin will end up being represented as ERC20 tokens on the Ethereum blockchain. +
+ ), + }, + { + prompt: '0x is open source: what prevents someone from forking the protocol?', + answer: ( +
+ Ethereum and Bitcoin are both open source protocols. Each protocol has been forked, but the + resulting clone networks have seen little adoption (as measured by transaction count or market + cap). This is because users have little to no incentive to switch over to a clone network if the + original has initial network effects and a talented developer team behind it. An exception is in + the case that a protocol includes a controversial feature such as a method of rent extraction or + a monetary policy that favors one group of users over another (Zcash developer subsidy - for + better or worse - resulted in Zclassic). Perceived inequality can provide a strong enough + incentive that users will fork the original protocol’s codebase and spin up a new network that + eliminates the controversial feature. In the case of 0x, there is no rent extraction and no + users are given special permissions. 0x protocol is upgradable. Cutting-edge technical + capabilities can be integrated into 0x via decentralized governance (see section below), + eliminating incentives to fork off of the original protocol and sacrifice the network effects + surrounding liquidity that result from the shared protocol and settlement layer. +
+ ), + }, + ], + }, + { + name: '0x Token (ZRX)', + questions: [ + { + prompt: 'Explain how the 0x protocol token (zrx) works.', + answer: ( +
+
+ 0x protocol token (ZRX) is utilized in two ways: 1) to solve the{' '} + + coordination problem + {' '} + and drive network effects around liquidity, creating a feedback loop where early adopters of + the protocol benefit from wider adoption and 2) to be used for decentralized governance over + 0x protocol's update mechanism. In more detail: +
+
    +
  • + ZRX tokens are used by Makers and Takers (market participants that generate and consume + orders, respectively) to pay transaction fees to Relayers (entities that host and + maintain public order books). +
  • +
  • + ZRX tokens are used for decentralized governance over 0x protocol’s update mechanism + which allows its underlying smart contracts to be replaced and improved over time. An + update mechanism is needed because 0x is built upon Ethereum’s rapidly evolving + technology stack, decentralized governance is needed because 0x protocol’s smart + contracts will have access to user funds and numerous dApps will need to plug into 0x + smart contracts. Decentralized governance ensures that this update process is secure and + minimizes disruption to the network. +
  • +
+
+ ), + }, + { + prompt: 'Why must transaction fees be denominated in 0x token (ZRX) rather than ETH?', + answer: ( +
+ 0x protocol’s decentralized update mechanism is analogous to proof-of-stake. To maximize the + alignment of stakeholder and end user incentives, the staked asset must provide utility within + the protocol. +
+ ), + }, + { + prompt: 'How will decentralized governance work?', + answer: ( +
+ Decentralized governance is an ongoing focus of research; it will involve token voting with ZRX. + Ultimately the solution will maximize security while also maximizing the protocol’s ability to + absorb new innovations. Until the governance structure is formalized and encoded within 0x DAO, + a multi-sig will be used as a placeholder. +
+ ), + }, + ], + }, + { + name: 'ZRX Token Launch and Fund Use', + questions: [ + { + prompt: 'What is the total supply of ZRX tokens?', + answer:
1,000,000,000 ZRX. Fixed supply.
, + }, + { + prompt: 'When is the Token Launch? will there be a pre-sale?', + answer:
The token launch will be on August 15th, 2017. There will not be a pre-sale.
, + }, + { + prompt: 'What will the token launch proceeds be used for?', + answer: ( +
+ 100% of the proceeds raised in the token launch will be used to fund the development of free and + open source software, tools and infrastructure that support the protocol and surrounding + ecosystem. Check out our{' '} + + development roadmap + . +
+ ), + }, + { + prompt: 'What will be the initial distribution of ZRX tokens?', + answer: ( +
+
+ +
+
+
Token Launch (50%)
+
+ ZRX is inherently a governance token that plays a critical role in the process of + upgrading 0x protocol. We are fully committed to formulating a functional and + theoretically sound governance model and we plan to dedicate significant resources to + R&D. +
+
+
+
Retained by 0x (15%)
+
+ The 0x core development team will be able to sustain itself for approximately five years + using funds raised through the token launch. If 0x protocol proves to be as foundational + a technology as we believe it to be, the retained ZRX tokens will allow the 0x core + development team to sustain operations beyond the first 5 years. +
+
+
+
Developer Fund (15%)
+
+ The Developer Fund will be used to make targeted capital injections into high potential + projects and teams that are attempting to grow the 0x ecosystem, strategic partnerships, + hackathon prizes and community development activities. +
+
+
+
Founding Team (10%)
+
+ The founding team’s allocation of ZRX will vest over a traditional 4 year vesting + schedule with a one year cliff. We believe this should be standard practice for any team + that is committed to making their project a long term success. +
+
+
+
Early Backers & Advisors (10%)
+
+ Our backers and advisors have provided capital, resources and guidance that have allowed + us to fill out our team, setup a robust legal entity and build a fully functional + product before launching a token. As a result, we have a proven track record and can + offer a token that holds genuine utility. +
+
+
+ ), + }, + { + prompt: 'Can I mine ZRX tokens?', + answer: ( +
+ No, the total supply of ZRX tokens is fixed and there is no continuous issuance model. Users + that facilitate trading over 0x protocol by operating a Relayer earn transaction fees + denominated in ZRX; as more trading activity is generated, more transaction fees are earned. +
+ ), + }, + { + prompt: 'Will there be a lockup period for ZRX tokens sold in the token launch?', + answer:
No, ZRX tokens sold in the token launch will immediately be liquid.
, + }, + { + prompt: 'Will there be a lockup period for tokens allocated to the founding team?', + answer: ( +
+ Yes. ZRX tokens allocated to founders, advisors and staff members will be released over a 4 year + vesting schedule with a 25% cliff upon completion of the initial token launch and 25% released + each subsequent year in monthly installments. Staff members hired after the token launch will + have a 4 year vesting schedule with a one year cliff. +
+ ), + }, + { + prompt: 'Which cryptocurrencies will be accepted in the token launch?', + answer:
ETH.
, + }, + { + prompt: 'When will 0x be live?', + answer: ( +
+ An alpha version of 0x has been live on our private test network since January 2017. Version 1.0 + of 0x protocol will be deployed to the canonical Ethereum blockchain after a round of security + audits and prior to the public token launch. 0x will be using the 0x protocol during our token + launch. +
+ ), + }, + { + prompt: 'Where can I find a development roadmap?', + answer: ( +
+ Check it out{' '} + + here + . +
+ ), + }, + ], + }, + { + name: 'Team', + questions: [ + { + prompt: 'Where is 0x based?', + answer:
0x was founded in SF and is driven by a diverse group of contributors.
, + }, + { + prompt: 'How can I get involved?', + answer: ( +
+ Join our{' '} + + Rocket.chat + ! As an open source project, 0x will rely on a worldwide community of passionate developers + to contribute proposals, ideas and code. +
+ ), + }, + { + prompt: 'Why the name 0x?', + answer: ( +
+ 0x is the prefix for hexadecimal numeric constants including Ethereum addresses. In a more + abstract context, as the first open protocol for exchange 0x represents the beginning of the end + for the exchange industry’s rent seeking oligopoly: zero exchange. +
+ ), + }, + { + prompt: 'How do you pronounce 0x?', + answer:
We pronounce 0x as “zero-ex,” but you are free to pronounce it however you please.
, + }, + ], + }, ]; export class FAQ extends React.Component { - public componentDidMount() { - window.scrollTo(0, 0); - } - public render() { - return ( -
- - -
-

- 0x FAQ -

-
{this._renderSections()}
-
-
-
- ); - } - private _renderSections() { - const renderedSections = _.map(sections, (section: FAQSection, i: number) => { - const isFirstSection = i === 0; - return ( -
-

{section.name}

- {this._renderQuestions(section.questions, isFirstSection)} -
- ); - }); - return renderedSections; - } - private _renderQuestions(questions: FAQQuestion[], isFirstSection: boolean) { - const renderedQuestions = _.map(questions, (question: FAQQuestion, i: number) => { - const isFirstQuestion = i === 0; - return ( - - ); - }); - return renderedQuestions; - } + public componentDidMount() { + window.scrollTo(0, 0); + } + public render() { + return ( +
+ + +
+

+ 0x FAQ +

+
{this._renderSections()}
+
+
+
+ ); + } + private _renderSections() { + const renderedSections = _.map(sections, (section: FAQSection, i: number) => { + const isFirstSection = i === 0; + return ( +
+

{section.name}

+ {this._renderQuestions(section.questions, isFirstSection)} +
+ ); + }); + return renderedSections; + } + private _renderQuestions(questions: FAQQuestion[], isFirstSection: boolean) { + const renderedQuestions = _.map(questions, (question: FAQQuestion, i: number) => { + const isFirstQuestion = i === 0; + return ( + + ); + }); + return renderedQuestions; + } } diff --git a/packages/website/ts/pages/faq/question.tsx b/packages/website/ts/pages/faq/question.tsx index 58cf674ef..988c04bc9 100644 --- a/packages/website/ts/pages/faq/question.tsx +++ b/packages/website/ts/pages/faq/question.tsx @@ -4,48 +4,48 @@ import * as React from 'react'; import { colors } from 'ts/utils/colors'; export interface QuestionProps { - prompt: string; - answer: React.ReactNode; - shouldDisplayExpanded: boolean; + prompt: string; + answer: React.ReactNode; + shouldDisplayExpanded: boolean; } interface QuestionState { - isExpanded: boolean; + isExpanded: boolean; } export class Question extends React.Component { - constructor(props: QuestionProps) { - super(props); - this.state = { - isExpanded: props.shouldDisplayExpanded, - }; - } - public render() { - return ( -
- - - -
{this.props.answer}
-
-
-
- ); - } - private _onExchangeChange() { - this.setState({ - isExpanded: !this.state.isExpanded, - }); - } + constructor(props: QuestionProps) { + super(props); + this.state = { + isExpanded: props.shouldDisplayExpanded, + }; + } + public render() { + return ( +
+ + + +
{this.props.answer}
+
+
+
+ ); + } + private _onExchangeChange() { + this.setState({ + isExpanded: !this.state.isExpanded, + }); + } } diff --git a/packages/website/ts/pages/landing/landing.tsx b/packages/website/ts/pages/landing/landing.tsx index 742d94a0f..ca76497df 100644 --- a/packages/website/ts/pages/landing/landing.tsx +++ b/packages/website/ts/pages/landing/landing.tsx @@ -11,755 +11,755 @@ import { constants } from 'ts/utils/constants'; import { utils } from 'ts/utils/utils'; interface BoxContent { - title: string; - description: string; - imageUrl: string; - classNames: string; + title: string; + description: string; + imageUrl: string; + classNames: string; } interface AssetType { - title: string; - imageUrl: string; - style?: React.CSSProperties; + title: string; + imageUrl: string; + style?: React.CSSProperties; } interface UseCase { - imageUrl: string; - type: string; - description: string; - classNames: string; - style?: React.CSSProperties; - projectIconUrls: string[]; + imageUrl: string; + type: string; + description: string; + classNames: string; + style?: React.CSSProperties; + projectIconUrls: string[]; } interface Project { - logoFileName: string; - projectUrl: string; + logoFileName: string; + projectUrl: string; } const THROTTLE_TIMEOUT = 100; const boxContents: BoxContent[] = [ - { - title: 'Trustless exchange', - description: - "Built on Ethereum's distributed network with no centralized \ + { + title: 'Trustless exchange', + description: + "Built on Ethereum's distributed network with no centralized \ point of failure and no down time, each trade is settled atomically \ and without counterparty risk.", - imageUrl: '/images/landing/distributed_network.png', - classNames: '', - }, - { - title: 'Shared liquidity', - description: - 'By sharing a standard API, relayers can easily aggregate liquidity pools, \ + imageUrl: '/images/landing/distributed_network.png', + classNames: '', + }, + { + title: 'Shared liquidity', + description: + 'By sharing a standard API, relayers can easily aggregate liquidity pools, \ creating network effects around liquidity that compound as more relayers come online.', - imageUrl: '/images/landing/liquidity.png', - classNames: 'mx-auto', - }, - { - title: 'Open source', - description: - '0x is open source, permissionless and free to use. Trade directly with a known \ + imageUrl: '/images/landing/liquidity.png', + classNames: 'mx-auto', + }, + { + title: 'Open source', + description: + '0x is open source, permissionless and free to use. Trade directly with a known \ counterparty for free or pay a relayer some ZRX tokens to access their liquidity \ pool.', - imageUrl: '/images/landing/open_source.png', - classNames: 'right', - }, + imageUrl: '/images/landing/open_source.png', + classNames: 'right', + }, ]; const projects: Project[] = [ - { - logoFileName: 'ethfinex-top.png', - projectUrl: constants.PROJECT_URL_ETHFINEX, - }, - { - logoFileName: 'radar_relay_top.png', - projectUrl: constants.PROJECT_URL_RADAR_RELAY, - }, - { - logoFileName: 'paradex_top.png', - projectUrl: constants.PROJECT_URL_PARADEX, - }, - { - logoFileName: 'the_ocean.png', - projectUrl: constants.PROJECT_URL_0CEAN, - }, - { - logoFileName: 'dydx.png', - projectUrl: constants.PROJECT_URL_DYDX, - }, - { - logoFileName: 'melonport.png', - projectUrl: constants.PROJECT_URL_MELONPORT, - }, - { - logoFileName: 'maker.png', - projectUrl: constants.PROJECT_URL_MAKER, - }, - { - logoFileName: 'dharma.png', - projectUrl: constants.PROJECT_URL_DHARMA, - }, - { - logoFileName: 'lendroid.png', - projectUrl: constants.PROJECT_URL_LENDROID, - }, - { - logoFileName: 'district0x.png', - projectUrl: constants.PROJECT_URL_DISTRICT_0X, - }, - { - logoFileName: 'aragon.png', - projectUrl: constants.PROJECT_URL_ARAGON, - }, - { - logoFileName: 'blocknet.png', - projectUrl: constants.PROJECT_URL_BLOCKNET, - }, - { - logoFileName: 'status.png', - projectUrl: constants.PROJECT_URL_STATUS, - }, - { - logoFileName: 'augur.png', - projectUrl: constants.PROJECT_URL_AUGUR, - }, - { - logoFileName: 'anx.png', - projectUrl: constants.PROJECT_URL_OPEN_ANX, - }, - { - logoFileName: 'auctus.png', - projectUrl: constants.PROJECT_URL_AUCTUS, - }, + { + logoFileName: 'ethfinex-top.png', + projectUrl: constants.PROJECT_URL_ETHFINEX, + }, + { + logoFileName: 'radar_relay_top.png', + projectUrl: constants.PROJECT_URL_RADAR_RELAY, + }, + { + logoFileName: 'paradex_top.png', + projectUrl: constants.PROJECT_URL_PARADEX, + }, + { + logoFileName: 'the_ocean.png', + projectUrl: constants.PROJECT_URL_0CEAN, + }, + { + logoFileName: 'dydx.png', + projectUrl: constants.PROJECT_URL_DYDX, + }, + { + logoFileName: 'melonport.png', + projectUrl: constants.PROJECT_URL_MELONPORT, + }, + { + logoFileName: 'maker.png', + projectUrl: constants.PROJECT_URL_MAKER, + }, + { + logoFileName: 'dharma.png', + projectUrl: constants.PROJECT_URL_DHARMA, + }, + { + logoFileName: 'lendroid.png', + projectUrl: constants.PROJECT_URL_LENDROID, + }, + { + logoFileName: 'district0x.png', + projectUrl: constants.PROJECT_URL_DISTRICT_0X, + }, + { + logoFileName: 'aragon.png', + projectUrl: constants.PROJECT_URL_ARAGON, + }, + { + logoFileName: 'blocknet.png', + projectUrl: constants.PROJECT_URL_BLOCKNET, + }, + { + logoFileName: 'status.png', + projectUrl: constants.PROJECT_URL_STATUS, + }, + { + logoFileName: 'augur.png', + projectUrl: constants.PROJECT_URL_AUGUR, + }, + { + logoFileName: 'anx.png', + projectUrl: constants.PROJECT_URL_OPEN_ANX, + }, + { + logoFileName: 'auctus.png', + projectUrl: constants.PROJECT_URL_AUCTUS, + }, ]; export interface LandingProps { - location: Location; + location: Location; } interface LandingState { - screenWidth: ScreenWidths; + screenWidth: ScreenWidths; } export class Landing extends React.Component { - private _throttledScreenWidthUpdate: () => void; - constructor(props: LandingProps) { - super(props); - this.state = { - screenWidth: utils.getScreenWidth(), - }; - this._throttledScreenWidthUpdate = _.throttle(this._updateScreenWidth.bind(this), THROTTLE_TIMEOUT); - } - public componentDidMount() { - window.addEventListener('resize', this._throttledScreenWidthUpdate); - window.scrollTo(0, 0); - } - public componentWillUnmount() { - window.removeEventListener('resize', this._throttledScreenWidthUpdate); - } - public render() { - return ( -
- - - {this._renderHero()} - {this._renderProjects()} - {this._renderTokenizationSection()} - {this._renderProtocolSection()} - {this._renderInfoBoxes()} - {this._renderBuildingBlocksSection()} - {this._renderUseCases()} - {this._renderCallToAction()} -
-
- ); - } - private _renderHero() { - const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; - const buttonLabelStyle: React.CSSProperties = { - textTransform: 'none', - fontSize: isSmallScreen ? 12 : 14, - fontWeight: 400, - }; - const lightButtonStyle: React.CSSProperties = { - borderRadius: 6, - border: '1px solid #D8D8D8', - lineHeight: '33px', - height: 38, - }; - const left = 'col lg-col-7 md-col-7 col-12 lg-pt4 md-pt4 sm-pt0 mt1 lg-pl4 md-pl4 sm-pl0 sm-px3 sm-center'; - return ( -
-
-
-
- -
-
-
-
- Powering decentralized exchange -
-
- 0x is an open, permissionless protocol allowing for ERC20 tokens to be traded on the - Ethereum blockchain. -
-
-
- - - -
-
- - - -
-
-
-
-
-
-
- ); - } - private _renderProjects() { - const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; - const isMediumScreen = this.state.screenWidth === ScreenWidths.Md; - const projectList = _.map(projects, (project: Project, i: number) => { - const colWidth = isSmallScreen ? 3 : isMediumScreen ? 4 : 2 - i % 2; - return ( -
-
- - - -
-
- ); - }); - const titleStyle: React.CSSProperties = { - fontFamily: 'Roboto Mono', - color: colors.grey, - textTransform: 'uppercase', - fontWeight: 300, - letterSpacing: 3, - }; - return ( -
-
-
- Projects building on 0x -
-
{projectList}
-
- view the{' '} - - full list - -
-
-
- ); - } - private _renderTokenizationSection() { - const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; - return ( -
-
- {isSmallScreen && this._renderTokenCloud()} -
-
-
- The world's value is becoming tokenized -
-
- {isSmallScreen ? ( - - The Ethereum blockchain is an open, borderless financial system that represents - a wide variety of assets as cryptographic tokens. In the future, most digital - assets and goods will be tokenized. - - ) : ( -
-
- The Ethereum blockchain is an open, borderless financial system that - represents -
-
- a wide variety of assets as cryptographic tokens. In the future, most - digital assets and goods will be tokenized. -
-
- )} -
-
{this._renderAssetTypes()}
-
-
- {!isSmallScreen && this._renderTokenCloud()} -
-
- ); - } - private _renderProtocolSection() { - const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; - return ( -
-
-
- -
-
-
-
Off-chain order relay
-
On-chain settlement
-
-
- In 0x protocol, orders are transported off-chain, massively reducing gas costs and - eliminating blockchain bloat. Relayers help broadcast orders and collect a fee each time - they facilitate a trade. Anyone can build a relayer. -
-
-
-
- RELAYERS BUILDING ON 0X -
-
- - view all - -
-
-
-
- -
-
- -
-
- -
-
-
-
-
-
- ); - } - private _renderBuildingBlocksSection() { - const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; - const descriptionStyle: React.CSSProperties = { - fontFamily: 'Roboto Mono', - lineHeight: isSmallScreen ? 1.5 : 2, - fontWeight: 300, - fontSize: 15, - maxWidth: isSmallScreen ? 375 : 'none', - }; - const callToActionStyle: React.CSSProperties = { - fontFamily: 'Roboto Mono', - fontSize: 15, - fontWeight: 300, - maxWidth: isSmallScreen ? 375 : 441, - }; - return ( -
-
- {isSmallScreen && this._renderBlockChipImage()} -
-
- A building block for dApps -
-
- 0x protocol is a pluggable building block for dApps that require exchange functionality. - Join the many developers that are already using 0x in their web applications and smart - contracts. -
-
- Learn how in our{' '} - - 0x.js - {' '} - and{' '} - - smart contract - {' '} - docs -
-
- {!isSmallScreen && this._renderBlockChipImage()} -
-
- ); - } - private _renderBlockChipImage() { - const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; - return ( -
- -
- ); - } - private _renderTokenCloud() { - const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; - return ( -
- -
- ); - } - private _renderAssetTypes() { - const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; - const assetTypes: AssetType[] = [ - { - title: 'Currency', - imageUrl: '/images/landing/currency.png', - }, - { - title: 'Traditional assets', - imageUrl: '/images/landing/stocks.png', - style: { - paddingLeft: isSmallScreen ? 41 : 56, - paddingRight: isSmallScreen ? 41 : 56, - }, - }, - { - title: 'Digital goods', - imageUrl: '/images/landing/digital_goods.png', - }, - ]; - const assets = _.map(assetTypes, (assetType: AssetType) => { - const style = _.isUndefined(assetType.style) ? {} : assetType.style; - return ( -
-
- -
-
- {assetType.title} -
-
- ); - }); - return assets; - } - private _renderInfoBoxes() { - const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; - const boxStyle: React.CSSProperties = { - maxWidth: 252, - height: 386, - backgroundColor: colors.grey50, - borderRadius: 5, - padding: '10px 24px 24px', - }; - const boxes = _.map(boxContents, (boxContent: BoxContent) => { - return ( -
-
-
- -
-
- {boxContent.title} -
-
- {boxContent.description} -
-
-
- ); - }); - return ( -
-
- {boxes} -
-
- ); - } - private _renderUseCases() { - const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; + private _throttledScreenWidthUpdate: () => void; + constructor(props: LandingProps) { + super(props); + this.state = { + screenWidth: utils.getScreenWidth(), + }; + this._throttledScreenWidthUpdate = _.throttle(this._updateScreenWidth.bind(this), THROTTLE_TIMEOUT); + } + public componentDidMount() { + window.addEventListener('resize', this._throttledScreenWidthUpdate); + window.scrollTo(0, 0); + } + public componentWillUnmount() { + window.removeEventListener('resize', this._throttledScreenWidthUpdate); + } + public render() { + return ( +
+ + + {this._renderHero()} + {this._renderProjects()} + {this._renderTokenizationSection()} + {this._renderProtocolSection()} + {this._renderInfoBoxes()} + {this._renderBuildingBlocksSection()} + {this._renderUseCases()} + {this._renderCallToAction()} +
+
+ ); + } + private _renderHero() { + const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; + const buttonLabelStyle: React.CSSProperties = { + textTransform: 'none', + fontSize: isSmallScreen ? 12 : 14, + fontWeight: 400, + }; + const lightButtonStyle: React.CSSProperties = { + borderRadius: 6, + border: '1px solid #D8D8D8', + lineHeight: '33px', + height: 38, + }; + const left = 'col lg-col-7 md-col-7 col-12 lg-pt4 md-pt4 sm-pt0 mt1 lg-pl4 md-pl4 sm-pl0 sm-px3 sm-center'; + return ( +
+
+
+
+ +
+
+
+
+ Powering decentralized exchange +
+
+ 0x is an open, permissionless protocol allowing for ERC20 tokens to be traded on the + Ethereum blockchain. +
+
+
+ + + +
+
+ + + +
+
+
+
+
+
+
+ ); + } + private _renderProjects() { + const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; + const isMediumScreen = this.state.screenWidth === ScreenWidths.Md; + const projectList = _.map(projects, (project: Project, i: number) => { + const colWidth = isSmallScreen ? 3 : isMediumScreen ? 4 : 2 - i % 2; + return ( +
+
+ + + +
+
+ ); + }); + const titleStyle: React.CSSProperties = { + fontFamily: 'Roboto Mono', + color: colors.grey, + textTransform: 'uppercase', + fontWeight: 300, + letterSpacing: 3, + }; + return ( +
+
+
+ Projects building on 0x +
+
{projectList}
+
+ view the{' '} + + full list + +
+
+
+ ); + } + private _renderTokenizationSection() { + const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; + return ( +
+
+ {isSmallScreen && this._renderTokenCloud()} +
+
+
+ The world's value is becoming tokenized +
+
+ {isSmallScreen ? ( + + The Ethereum blockchain is an open, borderless financial system that represents + a wide variety of assets as cryptographic tokens. In the future, most digital + assets and goods will be tokenized. + + ) : ( +
+
+ The Ethereum blockchain is an open, borderless financial system that + represents +
+
+ a wide variety of assets as cryptographic tokens. In the future, most + digital assets and goods will be tokenized. +
+
+ )} +
+
{this._renderAssetTypes()}
+
+
+ {!isSmallScreen && this._renderTokenCloud()} +
+
+ ); + } + private _renderProtocolSection() { + const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; + return ( +
+
+
+ +
+
+
+
Off-chain order relay
+
On-chain settlement
+
+
+ In 0x protocol, orders are transported off-chain, massively reducing gas costs and + eliminating blockchain bloat. Relayers help broadcast orders and collect a fee each time + they facilitate a trade. Anyone can build a relayer. +
+
+
+
+ RELAYERS BUILDING ON 0X +
+
+ + view all + +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+ ); + } + private _renderBuildingBlocksSection() { + const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; + const descriptionStyle: React.CSSProperties = { + fontFamily: 'Roboto Mono', + lineHeight: isSmallScreen ? 1.5 : 2, + fontWeight: 300, + fontSize: 15, + maxWidth: isSmallScreen ? 375 : 'none', + }; + const callToActionStyle: React.CSSProperties = { + fontFamily: 'Roboto Mono', + fontSize: 15, + fontWeight: 300, + maxWidth: isSmallScreen ? 375 : 441, + }; + return ( +
+
+ {isSmallScreen && this._renderBlockChipImage()} +
+
+ A building block for dApps +
+
+ 0x protocol is a pluggable building block for dApps that require exchange functionality. + Join the many developers that are already using 0x in their web applications and smart + contracts. +
+
+ Learn how in our{' '} + + 0x.js + {' '} + and{' '} + + smart contract + {' '} + docs +
+
+ {!isSmallScreen && this._renderBlockChipImage()} +
+
+ ); + } + private _renderBlockChipImage() { + const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; + return ( +
+ +
+ ); + } + private _renderTokenCloud() { + const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; + return ( +
+ +
+ ); + } + private _renderAssetTypes() { + const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; + const assetTypes: AssetType[] = [ + { + title: 'Currency', + imageUrl: '/images/landing/currency.png', + }, + { + title: 'Traditional assets', + imageUrl: '/images/landing/stocks.png', + style: { + paddingLeft: isSmallScreen ? 41 : 56, + paddingRight: isSmallScreen ? 41 : 56, + }, + }, + { + title: 'Digital goods', + imageUrl: '/images/landing/digital_goods.png', + }, + ]; + const assets = _.map(assetTypes, (assetType: AssetType) => { + const style = _.isUndefined(assetType.style) ? {} : assetType.style; + return ( +
+
+ +
+
+ {assetType.title} +
+
+ ); + }); + return assets; + } + private _renderInfoBoxes() { + const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; + const boxStyle: React.CSSProperties = { + maxWidth: 252, + height: 386, + backgroundColor: colors.grey50, + borderRadius: 5, + padding: '10px 24px 24px', + }; + const boxes = _.map(boxContents, (boxContent: BoxContent) => { + return ( +
+
+
+ +
+
+ {boxContent.title} +
+
+ {boxContent.description} +
+
+
+ ); + }); + return ( +
+
+ {boxes} +
+
+ ); + } + private _renderUseCases() { + const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; - const useCases: UseCase[] = [ - { - imageUrl: '/images/landing/governance_icon.png', - type: 'Decentralized governance', - description: - 'Decentralized organizations use tokens to represent ownership and \ + const useCases: UseCase[] = [ + { + imageUrl: '/images/landing/governance_icon.png', + type: 'Decentralized governance', + description: + 'Decentralized organizations use tokens to represent ownership and \ guide their governance logic. 0x allows decentralized organizations \ to seamlessly and safely trade ownership for startup capital.', - projectIconUrls: ['/images/landing/aragon.png'], - classNames: 'lg-px2 md-px2', - }, - { - imageUrl: '/images/landing/prediction_market_icon.png', - type: 'Prediction markets', - description: - 'Decentralized prediction market platforms generate sets of tokens that \ + projectIconUrls: ['/images/landing/aragon.png'], + classNames: 'lg-px2 md-px2', + }, + { + imageUrl: '/images/landing/prediction_market_icon.png', + type: 'Prediction markets', + description: + 'Decentralized prediction market platforms generate sets of tokens that \ represent a financial stake in the outcomes of real-world events. 0x allows \ these tokens to be instantly tradable.', - projectIconUrls: ['/images/landing/augur.png'], - classNames: 'lg-px2 md-px2', - }, - { - imageUrl: '/images/landing/stable_tokens_icon.png', - type: 'Stable tokens', - description: - 'Novel economic constructs such as stable coins require efficient, liquid \ + projectIconUrls: ['/images/landing/augur.png'], + classNames: 'lg-px2 md-px2', + }, + { + imageUrl: '/images/landing/stable_tokens_icon.png', + type: 'Stable tokens', + description: + 'Novel economic constructs such as stable coins require efficient, liquid \ markets to succeed. 0x will facilitate the underlying economic mechanisms \ that allow these tokens to remain stable.', - projectIconUrls: ['/images/landing/maker.png'], - classNames: 'lg-px2 md-px2', - }, - { - imageUrl: '/images/landing/loans_icon.png', - type: 'Decentralized loans', - description: - 'Efficient lending requires liquid markets where investors can buy and re-sell loans. \ + projectIconUrls: ['/images/landing/maker.png'], + classNames: 'lg-px2 md-px2', + }, + { + imageUrl: '/images/landing/loans_icon.png', + type: 'Decentralized loans', + description: + 'Efficient lending requires liquid markets where investors can buy and re-sell loans. \ 0x enables an ecosystem of lenders to self-organize and efficiently determine \ market prices for all outstanding loans.', - projectIconUrls: ['/images/landing/dharma.png', '/images/landing/lendroid.png'], - classNames: 'lg-pr2 md-pr2 lg-col-6 md-col-6', - style: { - width: 291, - float: 'right', - marginTop: !isSmallScreen ? 38 : 0, - }, - }, - { - imageUrl: '/images/landing/fund_management_icon.png', - type: 'Fund management', - description: - 'Decentralized fund management limits fund managers to investing in pre-agreed \ + projectIconUrls: ['/images/landing/dharma.png', '/images/landing/lendroid.png'], + classNames: 'lg-pr2 md-pr2 lg-col-6 md-col-6', + style: { + width: 291, + float: 'right', + marginTop: !isSmallScreen ? 38 : 0, + }, + }, + { + imageUrl: '/images/landing/fund_management_icon.png', + type: 'Fund management', + description: + 'Decentralized fund management limits fund managers to investing in pre-agreed \ upon asset classes. Embedding 0x into fund management smart contracts enables \ them to enforce these security constraints.', - projectIconUrls: ['/images/landing/melonport.png'], - classNames: 'lg-pl2 md-pl2 lg-col-6 md-col-6', - style: { width: 291, marginTop: !isSmallScreen ? 38 : 0 }, - }, - ]; + projectIconUrls: ['/images/landing/melonport.png'], + classNames: 'lg-pl2 md-pl2 lg-col-6 md-col-6', + style: { width: 291, marginTop: !isSmallScreen ? 38 : 0 }, + }, + ]; - const cases = _.map(useCases, (useCase: UseCase) => { - const style = _.isUndefined(useCase.style) || isSmallScreen ? {} : useCase.style; - const useCaseBoxStyle = { - color: colors.grey, - border: '1px solid #565656', - borderRadius: 4, - maxWidth: isSmallScreen ? 375 : 'none', - ...style, - }; - const typeStyle: React.CSSProperties = { - color: colors.lightGrey, - fontSize: 13, - textTransform: 'uppercase', - fontFamily: 'Roboto Mono', - fontWeight: 300, - }; - return ( -
-
-
- -
-
- {useCase.type} -
-
- {useCase.description} -
-
-
- ); - }); - return ( -
-
- {cases} -
-
- ); - } - private _renderCallToAction() { - const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; - const buttonLabelStyle: React.CSSProperties = { - textTransform: 'none', - fontSize: 15, - fontWeight: 400, - }; - const lightButtonStyle: React.CSSProperties = { - borderRadius: 6, - border: '1px solid #a0a0a0', - lineHeight: '33px', - height: 49, - }; - const callToActionClassNames = - 'col lg-col-8 md-col-8 col-12 lg-pr3 md-pr3 \ + const cases = _.map(useCases, (useCase: UseCase) => { + const style = _.isUndefined(useCase.style) || isSmallScreen ? {} : useCase.style; + const useCaseBoxStyle = { + color: colors.grey, + border: '1px solid #565656', + borderRadius: 4, + maxWidth: isSmallScreen ? 375 : 'none', + ...style, + }; + const typeStyle: React.CSSProperties = { + color: colors.lightGrey, + fontSize: 13, + textTransform: 'uppercase', + fontFamily: 'Roboto Mono', + fontWeight: 300, + }; + return ( +
+
+
+ +
+
+ {useCase.type} +
+
+ {useCase.description} +
+
+
+ ); + }); + return ( +
+
+ {cases} +
+
+ ); + } + private _renderCallToAction() { + const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; + const buttonLabelStyle: React.CSSProperties = { + textTransform: 'none', + fontSize: 15, + fontWeight: 400, + }; + const lightButtonStyle: React.CSSProperties = { + borderRadius: 6, + border: '1px solid #a0a0a0', + lineHeight: '33px', + height: 49, + }; + const callToActionClassNames = + 'col lg-col-8 md-col-8 col-12 lg-pr3 md-pr3 \ lg-right-align md-right-align sm-center sm-px3 h4'; - return ( -
-
-
- Get started on building the decentralized future -
-
- - - -
-
-
- ); - } - private _updateScreenWidth() { - const newScreenWidth = utils.getScreenWidth(); - if (newScreenWidth !== this.state.screenWidth) { - this.setState({ - screenWidth: newScreenWidth, - }); - } - } + return ( +
+
+
+ Get started on building the decentralized future +
+
+ + + +
+
+
+ ); + } + private _updateScreenWidth() { + const newScreenWidth = utils.getScreenWidth(); + if (newScreenWidth !== this.state.screenWidth) { + this.setState({ + screenWidth: newScreenWidth, + }); + } + } } // tslint:disable:max-file-line-count diff --git a/packages/website/ts/pages/not_found.tsx b/packages/website/ts/pages/not_found.tsx index 9d8d4142d..ff277c377 100644 --- a/packages/website/ts/pages/not_found.tsx +++ b/packages/website/ts/pages/not_found.tsx @@ -5,38 +5,38 @@ import { TopBar } from 'ts/components/top_bar'; import { Styles } from 'ts/types'; export interface NotFoundProps { - location: Location; + location: Location; } interface NotFoundState {} const styles: Styles = { - thin: { - fontWeight: 100, - }, + thin: { + fontWeight: 100, + }, }; export class NotFound extends React.Component { - public render() { - return ( -
- -
-
-
-
-

404 Not Found

-
-
- Hm... looks like we couldn't find what you are looking for. -
-
-
-
-
-
-
-
- ); - } + public render() { + return ( +
+ +
+
+
+
+

404 Not Found

+
+
+ Hm... looks like we couldn't find what you are looking for. +
+
+
+
+
+
+
+
+ ); + } } diff --git a/packages/website/ts/pages/shared/anchor_title.tsx b/packages/website/ts/pages/shared/anchor_title.tsx index 118aa0ea5..db5be1f59 100644 --- a/packages/website/ts/pages/shared/anchor_title.tsx +++ b/packages/website/ts/pages/shared/anchor_title.tsx @@ -5,87 +5,87 @@ import { constants } from 'ts/utils/constants'; import { utils } from 'ts/utils/utils'; const headerSizeToScrollOffset: { [headerSize: string]: number } = { - h2: -20, - h3: 0, + h2: -20, + h3: 0, }; interface AnchorTitleProps { - title: string | React.ReactNode; - id: string; - headerSize: HeaderSizes; - shouldShowAnchor: boolean; + title: string | React.ReactNode; + id: string; + headerSize: HeaderSizes; + shouldShowAnchor: boolean; } interface AnchorTitleState { - isHovering: boolean; + isHovering: boolean; } const styles: Styles = { - anchor: { - fontSize: 20, - transform: 'rotate(45deg)', - cursor: 'pointer', - }, - headers: { - WebkitMarginStart: 0, - WebkitMarginEnd: 0, - fontWeight: 'bold', - display: 'block', - }, - h1: { - fontSize: '1.8em', - WebkitMarginBefore: '0.83em', - WebkitMarginAfter: '0.83em', - }, - h2: { - fontSize: '1.5em', - WebkitMarginBefore: '0.83em', - WebkitMarginAfter: '0.83em', - }, - h3: { - fontSize: '1.17em', - WebkitMarginBefore: '1em', - WebkitMarginAfter: '1em', - }, + anchor: { + fontSize: 20, + transform: 'rotate(45deg)', + cursor: 'pointer', + }, + headers: { + WebkitMarginStart: 0, + WebkitMarginEnd: 0, + fontWeight: 'bold', + display: 'block', + }, + h1: { + fontSize: '1.8em', + WebkitMarginBefore: '0.83em', + WebkitMarginAfter: '0.83em', + }, + h2: { + fontSize: '1.5em', + WebkitMarginBefore: '0.83em', + WebkitMarginAfter: '0.83em', + }, + h3: { + fontSize: '1.17em', + WebkitMarginBefore: '1em', + WebkitMarginAfter: '1em', + }, }; export class AnchorTitle extends React.Component { - constructor(props: AnchorTitleProps) { - super(props); - this.state = { - isHovering: false, - }; - } - public render() { - let opacity = 0; - if (this.props.shouldShowAnchor) { - opacity = this.state.isHovering ? 0.6 : 1; - } - return ( -
-
- {this.props.title} -
- - - -
- ); - } - private _setHoverState(isHovering: boolean) { - this.setState({ - isHovering, - }); - } + constructor(props: AnchorTitleProps) { + super(props); + this.state = { + isHovering: false, + }; + } + public render() { + let opacity = 0; + if (this.props.shouldShowAnchor) { + opacity = this.state.isHovering ? 0.6 : 1; + } + return ( +
+
+ {this.props.title} +
+ + + +
+ ); + } + private _setHoverState(isHovering: boolean) { + this.setState({ + isHovering, + }); + } } diff --git a/packages/website/ts/pages/shared/markdown_code_block.tsx b/packages/website/ts/pages/shared/markdown_code_block.tsx index a9d95979b..be96fda16 100644 --- a/packages/website/ts/pages/shared/markdown_code_block.tsx +++ b/packages/website/ts/pages/shared/markdown_code_block.tsx @@ -3,23 +3,23 @@ import * as React from 'react'; import * as HighLight from 'react-highlight'; interface MarkdownCodeBlockProps { - literal: string; - language: string; + literal: string; + language: string; } interface MarkdownCodeBlockState {} export class MarkdownCodeBlock extends React.Component { - // Re-rendering a codeblock causes any use selection to become de-selected. This is annoying when trying - // to copy-paste code examples. We therefore noop re-renders on this component if it's props haven't changed. - public shouldComponentUpdate(nextProps: MarkdownCodeBlockProps, nextState: MarkdownCodeBlockState) { - return nextProps.literal !== this.props.literal || nextProps.language !== this.props.language; - } - public render() { - return ( - - {this.props.literal} - - ); - } + // Re-rendering a codeblock causes any use selection to become de-selected. This is annoying when trying + // to copy-paste code examples. We therefore noop re-renders on this component if it's props haven't changed. + public shouldComponentUpdate(nextProps: MarkdownCodeBlockProps, nextState: MarkdownCodeBlockState) { + return nextProps.literal !== this.props.literal || nextProps.language !== this.props.language; + } + public render() { + return ( + + {this.props.literal} + + ); + } } diff --git a/packages/website/ts/pages/shared/markdown_section.tsx b/packages/website/ts/pages/shared/markdown_section.tsx index c875ab736..5487dc8cc 100644 --- a/packages/website/ts/pages/shared/markdown_section.tsx +++ b/packages/website/ts/pages/shared/markdown_section.tsx @@ -9,66 +9,66 @@ import { HeaderSizes } from 'ts/types'; import { utils } from 'ts/utils/utils'; interface MarkdownSectionProps { - sectionName: string; - markdownContent: string; - headerSize?: HeaderSizes; - githubLink?: string; + sectionName: string; + markdownContent: string; + headerSize?: HeaderSizes; + githubLink?: string; } interface MarkdownSectionState { - shouldShowAnchor: boolean; + shouldShowAnchor: boolean; } export class MarkdownSection extends React.Component { - public static defaultProps: Partial = { - headerSize: HeaderSizes.H3, - }; - constructor(props: MarkdownSectionProps) { - super(props); - this.state = { - shouldShowAnchor: false, - }; - } - public render() { - const sectionName = this.props.sectionName; - const id = utils.getIdFromName(sectionName); - return ( -
- -
-
- - - -
-
- {!_.isUndefined(this.props.githubLink) && ( - } - /> - )} -
-
- -
-
- ); - } - private _setAnchorVisibility(shouldShowAnchor: boolean) { - this.setState({ - shouldShowAnchor, - }); - } + public static defaultProps: Partial = { + headerSize: HeaderSizes.H3, + }; + constructor(props: MarkdownSectionProps) { + super(props); + this.state = { + shouldShowAnchor: false, + }; + } + public render() { + const sectionName = this.props.sectionName; + const id = utils.getIdFromName(sectionName); + return ( +
+ +
+
+ + + +
+
+ {!_.isUndefined(this.props.githubLink) && ( + } + /> + )} +
+
+ +
+
+ ); + } + private _setAnchorVisibility(shouldShowAnchor: boolean) { + this.setState({ + shouldShowAnchor, + }); + } } diff --git a/packages/website/ts/pages/shared/nested_sidebar_menu.tsx b/packages/website/ts/pages/shared/nested_sidebar_menu.tsx index cd7ea68e6..849c33504 100644 --- a/packages/website/ts/pages/shared/nested_sidebar_menu.tsx +++ b/packages/website/ts/pages/shared/nested_sidebar_menu.tsx @@ -9,146 +9,146 @@ import { constants } from 'ts/utils/constants'; import { utils } from 'ts/utils/utils'; interface NestedSidebarMenuProps { - topLevelMenu: { [topLevel: string]: string[] }; - menuSubsectionsBySection: MenuSubsectionsBySection; - shouldDisplaySectionHeaders?: boolean; - onMenuItemClick?: () => void; - selectedVersion?: string; - versions?: string[]; - docPath?: string; - isSectionHeaderClickable?: boolean; + topLevelMenu: { [topLevel: string]: string[] }; + menuSubsectionsBySection: MenuSubsectionsBySection; + shouldDisplaySectionHeaders?: boolean; + onMenuItemClick?: () => void; + selectedVersion?: string; + versions?: string[]; + docPath?: string; + isSectionHeaderClickable?: boolean; } interface NestedSidebarMenuState {} const styles: Styles = { - menuItemWithHeaders: { - minHeight: 0, - }, - menuItemWithoutHeaders: { - minHeight: 48, - }, - menuItemInnerDivWithHeaders: { - lineHeight: 2, - }, + menuItemWithHeaders: { + minHeight: 0, + }, + menuItemWithoutHeaders: { + minHeight: 48, + }, + menuItemInnerDivWithHeaders: { + lineHeight: 2, + }, }; export class NestedSidebarMenu extends React.Component { - public static defaultProps: Partial = { - shouldDisplaySectionHeaders: true, - onMenuItemClick: _.noop, - }; - public render() { - const navigation = _.map(this.props.topLevelMenu, (menuItems: string[], sectionName: string) => { - const finalSectionName = sectionName.replace(/-/g, ' '); - if (this.props.shouldDisplaySectionHeaders) { - const id = utils.getIdFromName(sectionName); - return ( -
- -
- {finalSectionName.toUpperCase()} -
-
- {this._renderMenuItems(menuItems)} -
- ); - } else { - return
{this._renderMenuItems(menuItems)}
; - } - }); - return ( -
- {!_.isUndefined(this.props.versions) && - !_.isUndefined(this.props.selectedVersion) && - !_.isUndefined(this.props.docPath) && ( - - )} - {navigation} -
- ); - } - private _renderMenuItems(menuItemNames: string[]): React.ReactNode[] { - const menuItemStyles = this.props.shouldDisplaySectionHeaders - ? styles.menuItemWithHeaders - : styles.menuItemWithoutHeaders; - const menuItemInnerDivStyles = this.props.shouldDisplaySectionHeaders ? styles.menuItemInnerDivWithHeaders : {}; - const menuItems = _.map(menuItemNames, menuItemName => { - const id = utils.getIdFromName(menuItemName); - return ( -
- - - {menuItemName} - - - {this._renderMenuItemSubsections(menuItemName)} -
- ); - }); - return menuItems; - } - private _renderMenuItemSubsections(menuItemName: string): React.ReactNode { - if (_.isUndefined(this.props.menuSubsectionsBySection[menuItemName])) { - return null; - } - return this._renderMenuSubsectionsBySection(menuItemName, this.props.menuSubsectionsBySection[menuItemName]); - } - private _renderMenuSubsectionsBySection(menuItemName: string, entityNames: string[]): React.ReactNode { - return ( -
    - {_.map(entityNames, entityName => { - const name = `${menuItemName}-${entityName}`; - const id = utils.getIdFromName(name); - return ( -
  • - - - {entityName} - - -
  • - ); - })} -
- ); - } - private _onMenuItemClick(name: string): void { - const id = utils.getIdFromName(name); - utils.setUrlHash(id); - this.props.onMenuItemClick(); - } + public static defaultProps: Partial = { + shouldDisplaySectionHeaders: true, + onMenuItemClick: _.noop, + }; + public render() { + const navigation = _.map(this.props.topLevelMenu, (menuItems: string[], sectionName: string) => { + const finalSectionName = sectionName.replace(/-/g, ' '); + if (this.props.shouldDisplaySectionHeaders) { + const id = utils.getIdFromName(sectionName); + return ( +
+ +
+ {finalSectionName.toUpperCase()} +
+
+ {this._renderMenuItems(menuItems)} +
+ ); + } else { + return
{this._renderMenuItems(menuItems)}
; + } + }); + return ( +
+ {!_.isUndefined(this.props.versions) && + !_.isUndefined(this.props.selectedVersion) && + !_.isUndefined(this.props.docPath) && ( + + )} + {navigation} +
+ ); + } + private _renderMenuItems(menuItemNames: string[]): React.ReactNode[] { + const menuItemStyles = this.props.shouldDisplaySectionHeaders + ? styles.menuItemWithHeaders + : styles.menuItemWithoutHeaders; + const menuItemInnerDivStyles = this.props.shouldDisplaySectionHeaders ? styles.menuItemInnerDivWithHeaders : {}; + const menuItems = _.map(menuItemNames, menuItemName => { + const id = utils.getIdFromName(menuItemName); + return ( +
+ + + {menuItemName} + + + {this._renderMenuItemSubsections(menuItemName)} +
+ ); + }); + return menuItems; + } + private _renderMenuItemSubsections(menuItemName: string): React.ReactNode { + if (_.isUndefined(this.props.menuSubsectionsBySection[menuItemName])) { + return null; + } + return this._renderMenuSubsectionsBySection(menuItemName, this.props.menuSubsectionsBySection[menuItemName]); + } + private _renderMenuSubsectionsBySection(menuItemName: string, entityNames: string[]): React.ReactNode { + return ( +
    + {_.map(entityNames, entityName => { + const name = `${menuItemName}-${entityName}`; + const id = utils.getIdFromName(name); + return ( +
  • + + + {entityName} + + +
  • + ); + })} +
+ ); + } + private _onMenuItemClick(name: string): void { + const id = utils.getIdFromName(name); + utils.setUrlHash(id); + this.props.onMenuItemClick(); + } } diff --git a/packages/website/ts/pages/shared/section_header.tsx b/packages/website/ts/pages/shared/section_header.tsx index f9aa1a5e6..a5f5f52cf 100644 --- a/packages/website/ts/pages/shared/section_header.tsx +++ b/packages/website/ts/pages/shared/section_header.tsx @@ -5,46 +5,46 @@ import { HeaderSizes } from 'ts/types'; import { utils } from 'ts/utils/utils'; interface SectionHeaderProps { - sectionName: string; - headerSize?: HeaderSizes; + sectionName: string; + headerSize?: HeaderSizes; } interface SectionHeaderState { - shouldShowAnchor: boolean; + shouldShowAnchor: boolean; } export class SectionHeader extends React.Component { - public static defaultProps: Partial = { - headerSize: HeaderSizes.H2, - }; - constructor(props: SectionHeaderProps) { - super(props); - this.state = { - shouldShowAnchor: false, - }; - } - public render() { - const sectionName = this.props.sectionName.replace(/-/g, ' '); - const id = utils.getIdFromName(sectionName); - return ( -
- - {sectionName}} - id={id} - shouldShowAnchor={this.state.shouldShowAnchor} - /> - -
- ); - } - private _setAnchorVisibility(shouldShowAnchor: boolean) { - this.setState({ - shouldShowAnchor, - }); - } + public static defaultProps: Partial = { + headerSize: HeaderSizes.H2, + }; + constructor(props: SectionHeaderProps) { + super(props); + this.state = { + shouldShowAnchor: false, + }; + } + public render() { + const sectionName = this.props.sectionName.replace(/-/g, ' '); + const id = utils.getIdFromName(sectionName); + return ( +
+ + {sectionName}} + id={id} + shouldShowAnchor={this.state.shouldShowAnchor} + /> + +
+ ); + } + private _setAnchorVisibility(shouldShowAnchor: boolean) { + this.setState({ + shouldShowAnchor, + }); + } } diff --git a/packages/website/ts/pages/shared/version_drop_down.tsx b/packages/website/ts/pages/shared/version_drop_down.tsx index a647252ba..b922e1048 100644 --- a/packages/website/ts/pages/shared/version_drop_down.tsx +++ b/packages/website/ts/pages/shared/version_drop_down.tsx @@ -4,34 +4,34 @@ import MenuItem from 'material-ui/MenuItem'; import * as React from 'react'; interface VersionDropDownProps { - selectedVersion: string; - versions: string[]; - docPath: string; + selectedVersion: string; + versions: string[]; + docPath: string; } interface VersionDropDownState {} export class VersionDropDown extends React.Component { - public render() { - return ( -
- - {this._renderDropDownItems()} - -
- ); - } - private _renderDropDownItems() { - const items = _.map(this.props.versions, version => { - return ; - }); - return items; - } - private _updateSelectedVersion(e: any, index: number, value: string) { - window.location.href = `${this.props.docPath}/${value}${window.location.hash}`; - } + public render() { + return ( +
+ + {this._renderDropDownItems()} + +
+ ); + } + private _renderDropDownItems() { + const items = _.map(this.props.versions, version => { + return ; + }); + return items; + } + private _updateSelectedVersion(e: any, index: number, value: string) { + window.location.href = `${this.props.docPath}/${value}${window.location.hash}`; + } } diff --git a/packages/website/ts/pages/wiki/wiki.tsx b/packages/website/ts/pages/wiki/wiki.tsx index dba5ed6a0..d065614ba 100644 --- a/packages/website/ts/pages/wiki/wiki.tsx +++ b/packages/website/ts/pages/wiki/wiki.tsx @@ -16,186 +16,186 @@ import { utils } from 'ts/utils/utils'; const WIKI_NOT_READY_BACKOUT_TIMEOUT_MS = 5000; export interface WikiProps { - source: string; - location: Location; + source: string; + location: Location; } interface WikiState { - articlesBySection: ArticlesBySection; + articlesBySection: ArticlesBySection; } const styles: Styles = { - mainContainers: { - position: 'absolute', - top: 1, - left: 0, - bottom: 0, - right: 0, - overflowZ: 'hidden', - overflowY: 'scroll', - minHeight: 'calc(100vh - 1px)', - WebkitOverflowScrolling: 'touch', - }, - menuContainer: { - borderColor: colors.grey300, - maxWidth: 330, - marginLeft: 20, - }, + mainContainers: { + position: 'absolute', + top: 1, + left: 0, + bottom: 0, + right: 0, + overflowZ: 'hidden', + overflowY: 'scroll', + minHeight: 'calc(100vh - 1px)', + WebkitOverflowScrolling: 'touch', + }, + menuContainer: { + borderColor: colors.grey300, + maxWidth: 330, + marginLeft: 20, + }, }; export class Wiki extends React.Component { - private _wikiBackoffTimeoutId: number; - constructor(props: WikiProps) { - super(props); - this.state = { - articlesBySection: undefined, - }; - } - public componentWillMount() { - // tslint:disable-next-line:no-floating-promises - this._fetchArticlesBySectionAsync(); - } - public componentWillUnmount() { - clearTimeout(this._wikiBackoffTimeoutId); - } - public render() { - const menuSubsectionsBySection = _.isUndefined(this.state.articlesBySection) - ? {} - : this._getMenuSubsectionsBySection(this.state.articlesBySection); - return ( -
- - - {_.isUndefined(this.state.articlesBySection) ? ( -
-
-
- -
-
- Loading wiki... -
-
-
- ) : ( -
-
-
- -
-
-
-
-
-

- - 0x Protocol Wiki - -

-
{this._renderWikiArticles()}
-
-
-
- )} -
- ); - } - private _renderWikiArticles(): React.ReactNode { - const sectionNames = _.keys(this.state.articlesBySection); - const sections = _.map(sectionNames, sectionName => this._renderSection(sectionName)); - return sections; - } - private _renderSection(sectionName: string) { - const articles = this.state.articlesBySection[sectionName]; - const renderedArticles = _.map(articles, (article: Article) => { - const githubLink = `${constants.URL_GITHUB_WIKI}/edit/master/${sectionName}/${article.fileName}`; - return ( -
- -
- See a way to make this article better?{' '} - - Edit here → - -
-
- ); - }); - return ( -
- - {renderedArticles} -
- ); - } - private _scrollToHash(): void { - const hashWithPrefix = this.props.location.hash; - let hash = hashWithPrefix.slice(1); - if (_.isEmpty(hash)) { - hash = '0xProtocolWiki'; // scroll to the top - } + private _wikiBackoffTimeoutId: number; + constructor(props: WikiProps) { + super(props); + this.state = { + articlesBySection: undefined, + }; + } + public componentWillMount() { + // tslint:disable-next-line:no-floating-promises + this._fetchArticlesBySectionAsync(); + } + public componentWillUnmount() { + clearTimeout(this._wikiBackoffTimeoutId); + } + public render() { + const menuSubsectionsBySection = _.isUndefined(this.state.articlesBySection) + ? {} + : this._getMenuSubsectionsBySection(this.state.articlesBySection); + return ( +
+ + + {_.isUndefined(this.state.articlesBySection) ? ( +
+
+
+ +
+
+ Loading wiki... +
+
+
+ ) : ( +
+
+
+ +
+
+
+
+
+

+ + 0x Protocol Wiki + +

+
{this._renderWikiArticles()}
+
+
+
+ )} +
+ ); + } + private _renderWikiArticles(): React.ReactNode { + const sectionNames = _.keys(this.state.articlesBySection); + const sections = _.map(sectionNames, sectionName => this._renderSection(sectionName)); + return sections; + } + private _renderSection(sectionName: string) { + const articles = this.state.articlesBySection[sectionName]; + const renderedArticles = _.map(articles, (article: Article) => { + const githubLink = `${constants.URL_GITHUB_WIKI}/edit/master/${sectionName}/${article.fileName}`; + return ( +
+ +
+ See a way to make this article better?{' '} + + Edit here → + +
+
+ ); + }); + return ( +
+ + {renderedArticles} +
+ ); + } + private _scrollToHash(): void { + const hashWithPrefix = this.props.location.hash; + let hash = hashWithPrefix.slice(1); + if (_.isEmpty(hash)) { + hash = '0xProtocolWiki'; // scroll to the top + } - scroller.scrollTo(hash, { - duration: 0, - offset: 0, - containerId: 'documentation', - }); - } - private async _fetchArticlesBySectionAsync(): Promise { - const endpoint = `${configs.BACKEND_BASE_URL}${WebsitePaths.Wiki}`; - const response = await fetch(endpoint); - if (response.status === constants.HTTP_NO_CONTENT_STATUS_CODE) { - // We need to backoff and try fetching again later - this._wikiBackoffTimeoutId = window.setTimeout(() => { - // tslint:disable-next-line:no-floating-promises - this._fetchArticlesBySectionAsync(); - }, WIKI_NOT_READY_BACKOUT_TIMEOUT_MS); - return; - } - if (response.status !== 200) { - // TODO: Show the user an error message when the wiki fail to load - const errMsg = await response.text(); - utils.consoleLog(`Failed to load wiki: ${response.status} ${errMsg}`); - return; - } - const articlesBySection = await response.json(); - this.setState( - { - articlesBySection, - }, - () => { - this._scrollToHash(); - }, - ); - } - private _getMenuSubsectionsBySection(articlesBySection: ArticlesBySection) { - const sectionNames = _.keys(articlesBySection); - const menuSubsectionsBySection: { [section: string]: string[] } = {}; - for (const sectionName of sectionNames) { - const articles = articlesBySection[sectionName]; - const articleNames = _.map(articles, article => article.title); - menuSubsectionsBySection[sectionName] = articleNames; - } - return menuSubsectionsBySection; - } + scroller.scrollTo(hash, { + duration: 0, + offset: 0, + containerId: 'documentation', + }); + } + private async _fetchArticlesBySectionAsync(): Promise { + const endpoint = `${configs.BACKEND_BASE_URL}${WebsitePaths.Wiki}`; + const response = await fetch(endpoint); + if (response.status === constants.HTTP_NO_CONTENT_STATUS_CODE) { + // We need to backoff and try fetching again later + this._wikiBackoffTimeoutId = window.setTimeout(() => { + // tslint:disable-next-line:no-floating-promises + this._fetchArticlesBySectionAsync(); + }, WIKI_NOT_READY_BACKOUT_TIMEOUT_MS); + return; + } + if (response.status !== 200) { + // TODO: Show the user an error message when the wiki fail to load + const errMsg = await response.text(); + utils.consoleLog(`Failed to load wiki: ${response.status} ${errMsg}`); + return; + } + const articlesBySection = await response.json(); + this.setState( + { + articlesBySection, + }, + () => { + this._scrollToHash(); + }, + ); + } + private _getMenuSubsectionsBySection(articlesBySection: ArticlesBySection) { + const sectionNames = _.keys(articlesBySection); + const menuSubsectionsBySection: { [section: string]: string[] } = {}; + for (const sectionName of sectionNames) { + const articles = articlesBySection[sectionName]; + const articleNames = _.map(articles, article => article.title); + menuSubsectionsBySection[sectionName] = articleNames; + } + return menuSubsectionsBySection; + } } diff --git a/packages/website/ts/redux/dispatcher.ts b/packages/website/ts/redux/dispatcher.ts index 19300e242..42989e5e1 100644 --- a/packages/website/ts/redux/dispatcher.ts +++ b/packages/website/ts/redux/dispatcher.ts @@ -2,235 +2,235 @@ import { BigNumber } from '@0xproject/utils'; import { Dispatch } from 'redux'; import { State } from 'ts/redux/reducer'; import { - ActionTypes, - AssetToken, - BlockchainErrs, - Order, - ProviderType, - ScreenWidths, - Side, - SignatureData, - Token, - TokenStateByAddress, + ActionTypes, + AssetToken, + BlockchainErrs, + Order, + ProviderType, + ScreenWidths, + Side, + SignatureData, + Token, + TokenStateByAddress, } from 'ts/types'; export class Dispatcher { - private _dispatch: Dispatch; - constructor(dispatch: Dispatch) { - this._dispatch = dispatch; - } - // Portal - public resetState() { - this._dispatch({ - type: ActionTypes.ResetState, - }); - } - public updateNodeVersion(nodeVersion: string) { - this._dispatch({ - data: nodeVersion, - type: ActionTypes.UpdateNodeVersion, - }); - } - public updateScreenWidth(screenWidth: ScreenWidths) { - this._dispatch({ - data: screenWidth, - type: ActionTypes.UpdateScreenWidth, - }); - } - public swapAssetTokenSymbols() { - this._dispatch({ - type: ActionTypes.SwapAssetTokens, - }); - } - public updateOrderSalt(salt: BigNumber) { - this._dispatch({ - data: salt, - type: ActionTypes.UpdateOrderSalt, - }); - } - public updateUserSuppliedOrderCache(order: Order) { - this._dispatch({ - data: order, - type: ActionTypes.UpdateUserSuppliedOrderCache, - }); - } - public updateShouldBlockchainErrDialogBeOpen(shouldBeOpen: boolean) { - this._dispatch({ - data: shouldBeOpen, - type: ActionTypes.UpdateShouldBlockchainErrDialogBeOpen, - }); - } - public updateChosenAssetToken(side: Side, token: AssetToken) { - this._dispatch({ - data: { - side, - token, - }, - type: ActionTypes.UpdateChosenAssetToken, - }); - } - public updateChosenAssetTokenAddress(side: Side, address: string) { - this._dispatch({ - data: { - address, - side, - }, - type: ActionTypes.UpdateChosenAssetTokenAddress, - }); - } - public updateOrderTakerAddress(address: string) { - this._dispatch({ - data: address, - type: ActionTypes.UpdateOrderTakerAddress, - }); - } - public updateUserAddress(address: string) { - this._dispatch({ - data: address, - type: ActionTypes.UpdateUserAddress, - }); - } - public updateOrderExpiry(unixTimestampSec: BigNumber) { - this._dispatch({ - data: unixTimestampSec, - type: ActionTypes.UpdateOrderExpiry, - }); - } - public encounteredBlockchainError(err: BlockchainErrs) { - this._dispatch({ - data: err, - type: ActionTypes.BlockchainErrEncountered, - }); - } - public updateBlockchainIsLoaded(isLoaded: boolean) { - this._dispatch({ - data: isLoaded, - type: ActionTypes.UpdateBlockchainIsLoaded, - }); - } - public addTokenToTokenByAddress(token: Token) { - this._dispatch({ - data: token, - type: ActionTypes.AddTokenToTokenByAddress, - }); - } - public removeTokenToTokenByAddress(token: Token) { - this._dispatch({ - data: token, - type: ActionTypes.RemoveTokenFromTokenByAddress, - }); - } - public clearTokenByAddress() { - this._dispatch({ - type: ActionTypes.ClearTokenByAddress, - }); - } - public updateTokenByAddress(tokens: Token[]) { - this._dispatch({ - data: tokens, - type: ActionTypes.UpdateTokenByAddress, - }); - } - public updateTokenStateByAddress(tokenStateByAddress: TokenStateByAddress) { - this._dispatch({ - data: tokenStateByAddress, - type: ActionTypes.UpdateTokenStateByAddress, - }); - } - public removeFromTokenStateByAddress(tokenAddress: string) { - this._dispatch({ - data: tokenAddress, - type: ActionTypes.RemoveFromTokenStateByAddress, - }); - } - public replaceTokenAllowanceByAddress(address: string, allowance: BigNumber) { - this._dispatch({ - data: { - address, - allowance, - }, - type: ActionTypes.ReplaceTokenAllowanceByAddress, - }); - } - public replaceTokenBalanceByAddress(address: string, balance: BigNumber) { - this._dispatch({ - data: { - address, - balance, - }, - type: ActionTypes.ReplaceTokenBalanceByAddress, - }); - } - public updateTokenBalanceByAddress(address: string, balanceDelta: BigNumber) { - this._dispatch({ - data: { - address, - balanceDelta, - }, - type: ActionTypes.UpdateTokenBalanceByAddress, - }); - } - public updateSignatureData(signatureData: SignatureData) { - this._dispatch({ - data: signatureData, - type: ActionTypes.UpdateOrderSignatureData, - }); - } - public updateUserEtherBalance(balance: BigNumber) { - this._dispatch({ - data: balance, - type: ActionTypes.UpdateUserEtherBalance, - }); - } - public updateNetworkId(networkId: number) { - this._dispatch({ - data: networkId, - type: ActionTypes.UpdateNetworkId, - }); - } - public updateOrderFillAmount(amount: BigNumber) { - this._dispatch({ - data: amount, - type: ActionTypes.UpdateOrderFillAmount, - }); - } + private _dispatch: Dispatch; + constructor(dispatch: Dispatch) { + this._dispatch = dispatch; + } + // Portal + public resetState() { + this._dispatch({ + type: ActionTypes.ResetState, + }); + } + public updateNodeVersion(nodeVersion: string) { + this._dispatch({ + data: nodeVersion, + type: ActionTypes.UpdateNodeVersion, + }); + } + public updateScreenWidth(screenWidth: ScreenWidths) { + this._dispatch({ + data: screenWidth, + type: ActionTypes.UpdateScreenWidth, + }); + } + public swapAssetTokenSymbols() { + this._dispatch({ + type: ActionTypes.SwapAssetTokens, + }); + } + public updateOrderSalt(salt: BigNumber) { + this._dispatch({ + data: salt, + type: ActionTypes.UpdateOrderSalt, + }); + } + public updateUserSuppliedOrderCache(order: Order) { + this._dispatch({ + data: order, + type: ActionTypes.UpdateUserSuppliedOrderCache, + }); + } + public updateShouldBlockchainErrDialogBeOpen(shouldBeOpen: boolean) { + this._dispatch({ + data: shouldBeOpen, + type: ActionTypes.UpdateShouldBlockchainErrDialogBeOpen, + }); + } + public updateChosenAssetToken(side: Side, token: AssetToken) { + this._dispatch({ + data: { + side, + token, + }, + type: ActionTypes.UpdateChosenAssetToken, + }); + } + public updateChosenAssetTokenAddress(side: Side, address: string) { + this._dispatch({ + data: { + address, + side, + }, + type: ActionTypes.UpdateChosenAssetTokenAddress, + }); + } + public updateOrderTakerAddress(address: string) { + this._dispatch({ + data: address, + type: ActionTypes.UpdateOrderTakerAddress, + }); + } + public updateUserAddress(address: string) { + this._dispatch({ + data: address, + type: ActionTypes.UpdateUserAddress, + }); + } + public updateOrderExpiry(unixTimestampSec: BigNumber) { + this._dispatch({ + data: unixTimestampSec, + type: ActionTypes.UpdateOrderExpiry, + }); + } + public encounteredBlockchainError(err: BlockchainErrs) { + this._dispatch({ + data: err, + type: ActionTypes.BlockchainErrEncountered, + }); + } + public updateBlockchainIsLoaded(isLoaded: boolean) { + this._dispatch({ + data: isLoaded, + type: ActionTypes.UpdateBlockchainIsLoaded, + }); + } + public addTokenToTokenByAddress(token: Token) { + this._dispatch({ + data: token, + type: ActionTypes.AddTokenToTokenByAddress, + }); + } + public removeTokenToTokenByAddress(token: Token) { + this._dispatch({ + data: token, + type: ActionTypes.RemoveTokenFromTokenByAddress, + }); + } + public clearTokenByAddress() { + this._dispatch({ + type: ActionTypes.ClearTokenByAddress, + }); + } + public updateTokenByAddress(tokens: Token[]) { + this._dispatch({ + data: tokens, + type: ActionTypes.UpdateTokenByAddress, + }); + } + public updateTokenStateByAddress(tokenStateByAddress: TokenStateByAddress) { + this._dispatch({ + data: tokenStateByAddress, + type: ActionTypes.UpdateTokenStateByAddress, + }); + } + public removeFromTokenStateByAddress(tokenAddress: string) { + this._dispatch({ + data: tokenAddress, + type: ActionTypes.RemoveFromTokenStateByAddress, + }); + } + public replaceTokenAllowanceByAddress(address: string, allowance: BigNumber) { + this._dispatch({ + data: { + address, + allowance, + }, + type: ActionTypes.ReplaceTokenAllowanceByAddress, + }); + } + public replaceTokenBalanceByAddress(address: string, balance: BigNumber) { + this._dispatch({ + data: { + address, + balance, + }, + type: ActionTypes.ReplaceTokenBalanceByAddress, + }); + } + public updateTokenBalanceByAddress(address: string, balanceDelta: BigNumber) { + this._dispatch({ + data: { + address, + balanceDelta, + }, + type: ActionTypes.UpdateTokenBalanceByAddress, + }); + } + public updateSignatureData(signatureData: SignatureData) { + this._dispatch({ + data: signatureData, + type: ActionTypes.UpdateOrderSignatureData, + }); + } + public updateUserEtherBalance(balance: BigNumber) { + this._dispatch({ + data: balance, + type: ActionTypes.UpdateUserEtherBalance, + }); + } + public updateNetworkId(networkId: number) { + this._dispatch({ + data: networkId, + type: ActionTypes.UpdateNetworkId, + }); + } + public updateOrderFillAmount(amount: BigNumber) { + this._dispatch({ + data: amount, + type: ActionTypes.UpdateOrderFillAmount, + }); + } - // Docs - public updateCurrentDocsVersion(version: string) { - this._dispatch({ - data: version, - type: ActionTypes.UpdateLibraryVersion, - }); - } - public updateAvailableDocVersions(versions: string[]) { - this._dispatch({ - data: versions, - type: ActionTypes.UpdateAvailableLibraryVersions, - }); - } + // Docs + public updateCurrentDocsVersion(version: string) { + this._dispatch({ + data: version, + type: ActionTypes.UpdateLibraryVersion, + }); + } + public updateAvailableDocVersions(versions: string[]) { + this._dispatch({ + data: versions, + type: ActionTypes.UpdateAvailableLibraryVersions, + }); + } - // Shared - public showFlashMessage(msg: string | React.ReactNode) { - this._dispatch({ - data: msg, - type: ActionTypes.ShowFlashMessage, - }); - } - public hideFlashMessage() { - this._dispatch({ - type: ActionTypes.HideFlashMessage, - }); - } - public updateProviderType(providerType: ProviderType) { - this._dispatch({ - type: ActionTypes.UpdateProviderType, - data: providerType, - }); - } - public updateInjectedProviderName(injectedProviderName: string) { - this._dispatch({ - type: ActionTypes.UpdateInjectedProviderName, - data: injectedProviderName, - }); - } + // Shared + public showFlashMessage(msg: string | React.ReactNode) { + this._dispatch({ + data: msg, + type: ActionTypes.ShowFlashMessage, + }); + } + public hideFlashMessage() { + this._dispatch({ + type: ActionTypes.HideFlashMessage, + }); + } + public updateProviderType(providerType: ProviderType) { + this._dispatch({ + type: ActionTypes.UpdateProviderType, + data: providerType, + }); + } + public updateInjectedProviderName(injectedProviderName: string) { + this._dispatch({ + type: ActionTypes.UpdateInjectedProviderName, + data: injectedProviderName, + }); + } } diff --git a/packages/website/ts/redux/reducer.ts b/packages/website/ts/redux/reducer.ts index cc467eadd..06ac8b670 100644 --- a/packages/website/ts/redux/reducer.ts +++ b/packages/website/ts/redux/reducer.ts @@ -2,18 +2,18 @@ import { ZeroEx } from '0x.js'; import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; import { - Action, - ActionTypes, - BlockchainErrs, - Order, - ProviderType, - ScreenWidths, - Side, - SideToAssetToken, - SignatureData, - TokenByAddress, - TokenState, - TokenStateByAddress, + Action, + ActionTypes, + BlockchainErrs, + Order, + ProviderType, + ScreenWidths, + Side, + SideToAssetToken, + SignatureData, + TokenByAddress, + TokenState, + TokenStateByAddress, } from 'ts/types'; import { utils } from 'ts/utils/utils'; @@ -23,367 +23,367 @@ import { utils } from 'ts/utils/utils'; const DEFAULT_DOCS_VERSION = '0.0.0'; export interface State { - // Portal - blockchainErr: BlockchainErrs; - blockchainIsLoaded: boolean; - networkId: number; - orderExpiryTimestamp: BigNumber; - orderFillAmount: BigNumber; - orderTakerAddress: string; - orderSignatureData: SignatureData; - orderSalt: BigNumber; - nodeVersion: string; - screenWidth: ScreenWidths; - shouldBlockchainErrDialogBeOpen: boolean; - sideToAssetToken: SideToAssetToken; - tokenByAddress: TokenByAddress; - tokenStateByAddress: TokenStateByAddress; - userAddress: string; - userEtherBalance: BigNumber; - // Note: cache of supplied orderJSON in fill order step. Do not use for anything else. - userSuppliedOrderCache: Order; - - // Docs - docsVersion: string; - availableDocVersions: string[]; - - // Shared - flashMessage: string | React.ReactNode; - providerType: ProviderType; - injectedProviderName: string; + // Portal + blockchainErr: BlockchainErrs; + blockchainIsLoaded: boolean; + networkId: number; + orderExpiryTimestamp: BigNumber; + orderFillAmount: BigNumber; + orderTakerAddress: string; + orderSignatureData: SignatureData; + orderSalt: BigNumber; + nodeVersion: string; + screenWidth: ScreenWidths; + shouldBlockchainErrDialogBeOpen: boolean; + sideToAssetToken: SideToAssetToken; + tokenByAddress: TokenByAddress; + tokenStateByAddress: TokenStateByAddress; + userAddress: string; + userEtherBalance: BigNumber; + // Note: cache of supplied orderJSON in fill order step. Do not use for anything else. + userSuppliedOrderCache: Order; + + // Docs + docsVersion: string; + availableDocVersions: string[]; + + // Shared + flashMessage: string | React.ReactNode; + providerType: ProviderType; + injectedProviderName: string; } const INITIAL_STATE: State = { - // Portal - blockchainErr: BlockchainErrs.NoError, - blockchainIsLoaded: false, - networkId: undefined, - orderExpiryTimestamp: utils.initialOrderExpiryUnixTimestampSec(), - orderFillAmount: undefined, - orderSignatureData: { - hash: '', - r: '', - s: '', - v: 27, - }, - orderTakerAddress: '', - orderSalt: ZeroEx.generatePseudoRandomSalt(), - nodeVersion: undefined, - screenWidth: utils.getScreenWidth(), - shouldBlockchainErrDialogBeOpen: false, - sideToAssetToken: { - [Side.Deposit]: {}, - [Side.Receive]: {}, - }, - tokenByAddress: {}, - tokenStateByAddress: {}, - userAddress: '', - userEtherBalance: new BigNumber(0), - userSuppliedOrderCache: undefined, - - // Docs - docsVersion: DEFAULT_DOCS_VERSION, - availableDocVersions: [DEFAULT_DOCS_VERSION], - - // Shared - flashMessage: undefined, - providerType: ProviderType.Injected, - injectedProviderName: '', + // Portal + blockchainErr: BlockchainErrs.NoError, + blockchainIsLoaded: false, + networkId: undefined, + orderExpiryTimestamp: utils.initialOrderExpiryUnixTimestampSec(), + orderFillAmount: undefined, + orderSignatureData: { + hash: '', + r: '', + s: '', + v: 27, + }, + orderTakerAddress: '', + orderSalt: ZeroEx.generatePseudoRandomSalt(), + nodeVersion: undefined, + screenWidth: utils.getScreenWidth(), + shouldBlockchainErrDialogBeOpen: false, + sideToAssetToken: { + [Side.Deposit]: {}, + [Side.Receive]: {}, + }, + tokenByAddress: {}, + tokenStateByAddress: {}, + userAddress: '', + userEtherBalance: new BigNumber(0), + userSuppliedOrderCache: undefined, + + // Docs + docsVersion: DEFAULT_DOCS_VERSION, + availableDocVersions: [DEFAULT_DOCS_VERSION], + + // Shared + flashMessage: undefined, + providerType: ProviderType.Injected, + injectedProviderName: '', }; export function reducer(state: State = INITIAL_STATE, action: Action) { - switch (action.type) { - // Portal - case ActionTypes.ResetState: - return INITIAL_STATE; - - case ActionTypes.UpdateOrderSalt: { - return { - ...state, - orderSalt: action.data, - }; - } - - case ActionTypes.UpdateNodeVersion: { - return { - ...state, - nodeVersion: action.data, - }; - } - - case ActionTypes.UpdateOrderFillAmount: { - return { - ...state, - orderFillAmount: action.data, - }; - } - - case ActionTypes.UpdateShouldBlockchainErrDialogBeOpen: { - return { - ...state, - shouldBlockchainErrDialogBeOpen: action.data, - }; - } - - case ActionTypes.UpdateUserEtherBalance: { - return { - ...state, - userEtherBalance: action.data, - }; - } - - case ActionTypes.UpdateUserSuppliedOrderCache: { - return { - ...state, - userSuppliedOrderCache: action.data, - }; - } - - case ActionTypes.ClearTokenByAddress: { - return { - ...state, - tokenByAddress: {}, - }; - } - - case ActionTypes.AddTokenToTokenByAddress: { - const newTokenByAddress = state.tokenByAddress; - newTokenByAddress[action.data.address] = action.data; - return { - ...state, - tokenByAddress: newTokenByAddress, - }; - } - - case ActionTypes.RemoveTokenFromTokenByAddress: { - const newTokenByAddress = state.tokenByAddress; - delete newTokenByAddress[action.data.address]; - return { - ...state, - tokenByAddress: newTokenByAddress, - }; - } - - case ActionTypes.UpdateTokenByAddress: { - const tokenByAddress = state.tokenByAddress; - const tokens = action.data; - _.each(tokens, token => { - const updatedToken = { - ...tokenByAddress[token.address], - ...token, - }; - tokenByAddress[token.address] = updatedToken; - }); - return { - ...state, - tokenByAddress, - }; - } - - case ActionTypes.UpdateTokenStateByAddress: { - const tokenStateByAddress = state.tokenStateByAddress; - const updatedTokenStateByAddress = action.data; - _.each(updatedTokenStateByAddress, (tokenState: TokenState, address: string) => { - const updatedTokenState = { - ...tokenStateByAddress[address], - ...tokenState, - }; - tokenStateByAddress[address] = updatedTokenState; - }); - return { - ...state, - tokenStateByAddress, - }; - } - - case ActionTypes.RemoveFromTokenStateByAddress: { - const tokenStateByAddress = state.tokenStateByAddress; - const tokenAddress = action.data; - delete tokenStateByAddress[tokenAddress]; - return { - ...state, - tokenStateByAddress, - }; - } - - case ActionTypes.ReplaceTokenAllowanceByAddress: { - const tokenStateByAddress = state.tokenStateByAddress; - const allowance = action.data.allowance; - const tokenAddress = action.data.address; - tokenStateByAddress[tokenAddress] = { - ...tokenStateByAddress[tokenAddress], - allowance, - }; - return { - ...state, - tokenStateByAddress, - }; - } - - case ActionTypes.ReplaceTokenBalanceByAddress: { - const tokenStateByAddress = state.tokenStateByAddress; - const balance = action.data.balance; - const tokenAddress = action.data.address; - tokenStateByAddress[tokenAddress] = { - ...tokenStateByAddress[tokenAddress], - balance, - }; - return { - ...state, - tokenStateByAddress, - }; - } - - case ActionTypes.UpdateTokenBalanceByAddress: { - const tokenStateByAddress = state.tokenStateByAddress; - const balanceDelta = action.data.balanceDelta; - const tokenAddress = action.data.address; - const currBalance = tokenStateByAddress[tokenAddress].balance; - tokenStateByAddress[tokenAddress] = { - ...tokenStateByAddress[tokenAddress], - balance: currBalance.plus(balanceDelta), - }; - return { - ...state, - tokenStateByAddress, - }; - } - - case ActionTypes.UpdateOrderSignatureData: { - return { - ...state, - orderSignatureData: action.data, - }; - } - - case ActionTypes.UpdateScreenWidth: { - return { - ...state, - screenWidth: action.data, - }; - } - - case ActionTypes.UpdateBlockchainIsLoaded: { - return { - ...state, - blockchainIsLoaded: action.data, - }; - } - - case ActionTypes.BlockchainErrEncountered: { - return { - ...state, - blockchainErr: action.data, - }; - } - - case ActionTypes.UpdateNetworkId: { - return { - ...state, - networkId: action.data, - }; - } - - case ActionTypes.UpdateChosenAssetToken: { - const newSideToAssetToken = { - ...state.sideToAssetToken, - [action.data.side]: action.data.token, - }; - return { - ...state, - sideToAssetToken: newSideToAssetToken, - }; - } - - case ActionTypes.UpdateChosenAssetTokenAddress: { - const newAssetToken = state.sideToAssetToken[action.data.side]; - newAssetToken.address = action.data.address; - const newSideToAssetToken = { - ...state.sideToAssetToken, - [action.data.side]: newAssetToken, - }; - return { - ...state, - sideToAssetToken: newSideToAssetToken, - }; - } - - case ActionTypes.SwapAssetTokens: { - const newSideToAssetToken = { - [Side.Deposit]: state.sideToAssetToken[Side.Receive], - [Side.Receive]: state.sideToAssetToken[Side.Deposit], - }; - return { - ...state, - sideToAssetToken: newSideToAssetToken, - }; - } - - case ActionTypes.UpdateOrderExpiry: { - return { - ...state, - orderExpiryTimestamp: action.data, - }; - } - - case ActionTypes.UpdateOrderTakerAddress: { - return { - ...state, - orderTakerAddress: action.data, - }; - } - - case ActionTypes.UpdateUserAddress: { - return { - ...state, - userAddress: action.data, - }; - } - - // Docs - case ActionTypes.UpdateLibraryVersion: { - return { - ...state, - docsVersion: action.data, - }; - } - case ActionTypes.UpdateAvailableLibraryVersions: { - return { - ...state, - availableDocVersions: action.data, - }; - } - - // Shared - case ActionTypes.ShowFlashMessage: { - return { - ...state, - flashMessage: action.data, - }; - } - - case ActionTypes.HideFlashMessage: { - return { - ...state, - flashMessage: undefined, - }; - } - - case ActionTypes.UpdateProviderType: { - return { - ...state, - providerType: action.data, - }; - } - - case ActionTypes.UpdateInjectedProviderName: { - return { - ...state, - injectedProviderName: action.data, - }; - } - - default: - return state; - } + switch (action.type) { + // Portal + case ActionTypes.ResetState: + return INITIAL_STATE; + + case ActionTypes.UpdateOrderSalt: { + return { + ...state, + orderSalt: action.data, + }; + } + + case ActionTypes.UpdateNodeVersion: { + return { + ...state, + nodeVersion: action.data, + }; + } + + case ActionTypes.UpdateOrderFillAmount: { + return { + ...state, + orderFillAmount: action.data, + }; + } + + case ActionTypes.UpdateShouldBlockchainErrDialogBeOpen: { + return { + ...state, + shouldBlockchainErrDialogBeOpen: action.data, + }; + } + + case ActionTypes.UpdateUserEtherBalance: { + return { + ...state, + userEtherBalance: action.data, + }; + } + + case ActionTypes.UpdateUserSuppliedOrderCache: { + return { + ...state, + userSuppliedOrderCache: action.data, + }; + } + + case ActionTypes.ClearTokenByAddress: { + return { + ...state, + tokenByAddress: {}, + }; + } + + case ActionTypes.AddTokenToTokenByAddress: { + const newTokenByAddress = state.tokenByAddress; + newTokenByAddress[action.data.address] = action.data; + return { + ...state, + tokenByAddress: newTokenByAddress, + }; + } + + case ActionTypes.RemoveTokenFromTokenByAddress: { + const newTokenByAddress = state.tokenByAddress; + delete newTokenByAddress[action.data.address]; + return { + ...state, + tokenByAddress: newTokenByAddress, + }; + } + + case ActionTypes.UpdateTokenByAddress: { + const tokenByAddress = state.tokenByAddress; + const tokens = action.data; + _.each(tokens, token => { + const updatedToken = { + ...tokenByAddress[token.address], + ...token, + }; + tokenByAddress[token.address] = updatedToken; + }); + return { + ...state, + tokenByAddress, + }; + } + + case ActionTypes.UpdateTokenStateByAddress: { + const tokenStateByAddress = state.tokenStateByAddress; + const updatedTokenStateByAddress = action.data; + _.each(updatedTokenStateByAddress, (tokenState: TokenState, address: string) => { + const updatedTokenState = { + ...tokenStateByAddress[address], + ...tokenState, + }; + tokenStateByAddress[address] = updatedTokenState; + }); + return { + ...state, + tokenStateByAddress, + }; + } + + case ActionTypes.RemoveFromTokenStateByAddress: { + const tokenStateByAddress = state.tokenStateByAddress; + const tokenAddress = action.data; + delete tokenStateByAddress[tokenAddress]; + return { + ...state, + tokenStateByAddress, + }; + } + + case ActionTypes.ReplaceTokenAllowanceByAddress: { + const tokenStateByAddress = state.tokenStateByAddress; + const allowance = action.data.allowance; + const tokenAddress = action.data.address; + tokenStateByAddress[tokenAddress] = { + ...tokenStateByAddress[tokenAddress], + allowance, + }; + return { + ...state, + tokenStateByAddress, + }; + } + + case ActionTypes.ReplaceTokenBalanceByAddress: { + const tokenStateByAddress = state.tokenStateByAddress; + const balance = action.data.balance; + const tokenAddress = action.data.address; + tokenStateByAddress[tokenAddress] = { + ...tokenStateByAddress[tokenAddress], + balance, + }; + return { + ...state, + tokenStateByAddress, + }; + } + + case ActionTypes.UpdateTokenBalanceByAddress: { + const tokenStateByAddress = state.tokenStateByAddress; + const balanceDelta = action.data.balanceDelta; + const tokenAddress = action.data.address; + const currBalance = tokenStateByAddress[tokenAddress].balance; + tokenStateByAddress[tokenAddress] = { + ...tokenStateByAddress[tokenAddress], + balance: currBalance.plus(balanceDelta), + }; + return { + ...state, + tokenStateByAddress, + }; + } + + case ActionTypes.UpdateOrderSignatureData: { + return { + ...state, + orderSignatureData: action.data, + }; + } + + case ActionTypes.UpdateScreenWidth: { + return { + ...state, + screenWidth: action.data, + }; + } + + case ActionTypes.UpdateBlockchainIsLoaded: { + return { + ...state, + blockchainIsLoaded: action.data, + }; + } + + case ActionTypes.BlockchainErrEncountered: { + return { + ...state, + blockchainErr: action.data, + }; + } + + case ActionTypes.UpdateNetworkId: { + return { + ...state, + networkId: action.data, + }; + } + + case ActionTypes.UpdateChosenAssetToken: { + const newSideToAssetToken = { + ...state.sideToAssetToken, + [action.data.side]: action.data.token, + }; + return { + ...state, + sideToAssetToken: newSideToAssetToken, + }; + } + + case ActionTypes.UpdateChosenAssetTokenAddress: { + const newAssetToken = state.sideToAssetToken[action.data.side]; + newAssetToken.address = action.data.address; + const newSideToAssetToken = { + ...state.sideToAssetToken, + [action.data.side]: newAssetToken, + }; + return { + ...state, + sideToAssetToken: newSideToAssetToken, + }; + } + + case ActionTypes.SwapAssetTokens: { + const newSideToAssetToken = { + [Side.Deposit]: state.sideToAssetToken[Side.Receive], + [Side.Receive]: state.sideToAssetToken[Side.Deposit], + }; + return { + ...state, + sideToAssetToken: newSideToAssetToken, + }; + } + + case ActionTypes.UpdateOrderExpiry: { + return { + ...state, + orderExpiryTimestamp: action.data, + }; + } + + case ActionTypes.UpdateOrderTakerAddress: { + return { + ...state, + orderTakerAddress: action.data, + }; + } + + case ActionTypes.UpdateUserAddress: { + return { + ...state, + userAddress: action.data, + }; + } + + // Docs + case ActionTypes.UpdateLibraryVersion: { + return { + ...state, + docsVersion: action.data, + }; + } + case ActionTypes.UpdateAvailableLibraryVersions: { + return { + ...state, + availableDocVersions: action.data, + }; + } + + // Shared + case ActionTypes.ShowFlashMessage: { + return { + ...state, + flashMessage: action.data, + }; + } + + case ActionTypes.HideFlashMessage: { + return { + ...state, + flashMessage: undefined, + }; + } + + case ActionTypes.UpdateProviderType: { + return { + ...state, + providerType: action.data, + }; + } + + case ActionTypes.UpdateInjectedProviderName: { + return { + ...state, + injectedProviderName: action.data, + }; + } + + default: + return state; + } } diff --git a/packages/website/ts/schemas/order_schema.ts b/packages/website/ts/schemas/order_schema.ts index fd0bf113a..bfbf9eb8b 100644 --- a/packages/website/ts/schemas/order_schema.ts +++ b/packages/website/ts/schemas/order_schema.ts @@ -1,15 +1,15 @@ export const orderSchema = { - id: '/Order', - properties: { - maker: { $ref: '/OrderTaker' }, - taker: { $ref: '/OrderTaker' }, - salt: { type: 'string' }, - signature: { $ref: '/SignatureData' }, - expiration: { type: 'string' }, - feeRecipient: { type: 'string' }, - exchangeContract: { type: 'string' }, - networkId: { type: 'number' }, - }, - required: ['maker', 'taker', 'salt', 'signature', 'expiration', 'feeRecipient', 'exchangeContract', 'networkId'], - type: 'object', + id: '/Order', + properties: { + maker: { $ref: '/OrderTaker' }, + taker: { $ref: '/OrderTaker' }, + salt: { type: 'string' }, + signature: { $ref: '/SignatureData' }, + expiration: { type: 'string' }, + feeRecipient: { type: 'string' }, + exchangeContract: { type: 'string' }, + networkId: { type: 'number' }, + }, + required: ['maker', 'taker', 'salt', 'signature', 'expiration', 'feeRecipient', 'exchangeContract', 'networkId'], + type: 'object', }; diff --git a/packages/website/ts/schemas/order_taker_schema.ts b/packages/website/ts/schemas/order_taker_schema.ts index c84ec4a9f..c784c29c5 100644 --- a/packages/website/ts/schemas/order_taker_schema.ts +++ b/packages/website/ts/schemas/order_taker_schema.ts @@ -1,11 +1,11 @@ export const orderTakerSchema = { - id: '/OrderTaker', - properties: { - address: { type: 'string' }, - token: { $ref: '/Token' }, - amount: { type: 'string' }, - feeAmount: { type: 'string' }, - }, - required: ['address', 'token', 'amount', 'feeAmount'], - type: 'object', + id: '/OrderTaker', + properties: { + address: { type: 'string' }, + token: { $ref: '/Token' }, + amount: { type: 'string' }, + feeAmount: { type: 'string' }, + }, + required: ['address', 'token', 'amount', 'feeAmount'], + type: 'object', }; diff --git a/packages/website/ts/schemas/signature_data_schema.ts b/packages/website/ts/schemas/signature_data_schema.ts index 8cafff9e8..8d3f15926 100644 --- a/packages/website/ts/schemas/signature_data_schema.ts +++ b/packages/website/ts/schemas/signature_data_schema.ts @@ -1,11 +1,11 @@ export const signatureDataSchema = { - id: '/SignatureData', - properties: { - hash: { type: 'string' }, - r: { type: 'string' }, - s: { type: 'string' }, - v: { type: 'number' }, - }, - required: ['hash', 'r', 's', 'v'], - type: 'object', + id: '/SignatureData', + properties: { + hash: { type: 'string' }, + r: { type: 'string' }, + s: { type: 'string' }, + v: { type: 'number' }, + }, + required: ['hash', 'r', 's', 'v'], + type: 'object', }; diff --git a/packages/website/ts/schemas/token_schema.ts b/packages/website/ts/schemas/token_schema.ts index 6a3bed786..92b53a463 100644 --- a/packages/website/ts/schemas/token_schema.ts +++ b/packages/website/ts/schemas/token_schema.ts @@ -1,11 +1,11 @@ export const tokenSchema = { - id: '/Token', - properties: { - name: { type: 'string' }, - symbol: { type: 'string' }, - decimals: { type: 'number' }, - address: { type: 'string' }, - }, - required: ['name', 'symbol', 'decimals', 'address'], - type: 'object', + id: '/Token', + properties: { + name: { type: 'string' }, + symbol: { type: 'string' }, + decimals: { type: 'number' }, + address: { type: 'string' }, + }, + required: ['name', 'symbol', 'decimals', 'address'], + type: 'object', }; diff --git a/packages/website/ts/schemas/validator.ts b/packages/website/ts/schemas/validator.ts index a3aaafc98..5177501c6 100644 --- a/packages/website/ts/schemas/validator.ts +++ b/packages/website/ts/schemas/validator.ts @@ -5,15 +5,15 @@ import { signatureDataSchema } from 'ts/schemas/signature_data_schema'; import { tokenSchema } from 'ts/schemas/token_schema'; export class SchemaValidator { - private _validator: Validator; - constructor() { - this._validator = new Validator(); - this._validator.addSchema(signatureDataSchema as JSONSchema, signatureDataSchema.id); - this._validator.addSchema(tokenSchema as JSONSchema, tokenSchema.id); - this._validator.addSchema(orderTakerSchema as JSONSchema, orderTakerSchema.id); - this._validator.addSchema(orderSchema as JSONSchema, orderSchema.id); - } - public validate(instance: object, schema: Schema) { - return this._validator.validate(instance, schema); - } + private _validator: Validator; + constructor() { + this._validator = new Validator(); + this._validator.addSchema(signatureDataSchema as JSONSchema, signatureDataSchema.id); + this._validator.addSchema(tokenSchema as JSONSchema, tokenSchema.id); + this._validator.addSchema(orderTakerSchema as JSONSchema, orderTakerSchema.id); + this._validator.addSchema(orderSchema as JSONSchema, orderSchema.id); + } + public validate(instance: object, schema: Schema) { + return this._validator.validate(instance, schema); + } } diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index a853792cb..f873f95fa 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -2,680 +2,680 @@ import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; export enum Side { - Receive = 'RECEIVE', - Deposit = 'DEPOSIT', + Receive = 'RECEIVE', + Deposit = 'DEPOSIT', } export interface Token { - iconUrl?: string; - name: string; - address: string; - symbol: string; - decimals: number; - isTracked: boolean; - isRegistered: boolean; + iconUrl?: string; + name: string; + address: string; + symbol: string; + decimals: number; + isTracked: boolean; + isRegistered: boolean; } export interface TokenByAddress { - [address: string]: Token; + [address: string]: Token; } export interface TokenState { - allowance: BigNumber; - balance: BigNumber; + allowance: BigNumber; + balance: BigNumber; } export interface TokenStateByAddress { - [address: string]: TokenState; + [address: string]: TokenState; } export interface AssetToken { - address?: string; - amount?: BigNumber; + address?: string; + amount?: BigNumber; } export interface SideToAssetToken { - [side: string]: AssetToken; + [side: string]: AssetToken; } export interface SignatureData { - hash: string; - r: string; - s: string; - v: number; + hash: string; + r: string; + s: string; + v: number; } export interface HashData { - depositAmount: BigNumber; - depositTokenContractAddr: string; - feeRecipientAddress: string; - makerFee: BigNumber; - orderExpiryTimestamp: BigNumber; - orderMakerAddress: string; - orderTakerAddress: string; - receiveAmount: BigNumber; - receiveTokenContractAddr: string; - takerFee: BigNumber; - orderSalt: BigNumber; + depositAmount: BigNumber; + depositTokenContractAddr: string; + feeRecipientAddress: string; + makerFee: BigNumber; + orderExpiryTimestamp: BigNumber; + orderMakerAddress: string; + orderTakerAddress: string; + receiveAmount: BigNumber; + receiveTokenContractAddr: string; + takerFee: BigNumber; + orderSalt: BigNumber; } export interface OrderToken { - name: string; - symbol: string; - decimals: number; - address: string; + name: string; + symbol: string; + decimals: number; + address: string; } export interface OrderParty { - address: string; - token: OrderToken; - amount: string; - feeAmount: string; + address: string; + token: OrderToken; + amount: string; + feeAmount: string; } export interface Order { - maker: OrderParty; - taker: OrderParty; - expiration: string; - feeRecipient: string; - salt: string; - signature: SignatureData; - exchangeContract: string; - networkId: number; + maker: OrderParty; + taker: OrderParty; + expiration: string; + feeRecipient: string; + salt: string; + signature: SignatureData; + exchangeContract: string; + networkId: number; } export interface Fill { - logIndex: number; - maker: string; - taker: string; - makerToken: string; - takerToken: string; - filledMakerTokenAmount: BigNumber; - filledTakerTokenAmount: BigNumber; - paidMakerFee: BigNumber; - paidTakerFee: BigNumber; - orderHash: string; - transactionHash: string; - blockTimestamp: number; + logIndex: number; + maker: string; + taker: string; + makerToken: string; + takerToken: string; + filledMakerTokenAmount: BigNumber; + filledTakerTokenAmount: BigNumber; + paidMakerFee: BigNumber; + paidTakerFee: BigNumber; + orderHash: string; + transactionHash: string; + blockTimestamp: number; } export enum BalanceErrs { - incorrectNetworkForFaucet, - faucetRequestFailed, - faucetQueueIsFull, - mintingFailed, - sendFailed, - allowanceSettingFailed, + incorrectNetworkForFaucet, + faucetRequestFailed, + faucetQueueIsFull, + mintingFailed, + sendFailed, + allowanceSettingFailed, } export enum ActionTypes { - // Portal - UpdateScreenWidth = 'UPDATE_SCREEN_WIDTH', - UpdateNodeVersion = 'UPDATE_NODE_VERSION', - ResetState = 'RESET_STATE', - AddTokenToTokenByAddress = 'ADD_TOKEN_TO_TOKEN_BY_ADDRESS', - BlockchainErrEncountered = 'BLOCKCHAIN_ERR_ENCOUNTERED', - ClearTokenByAddress = 'CLEAR_TOKEN_BY_ADDRESS', - UpdateBlockchainIsLoaded = 'UPDATE_BLOCKCHAIN_IS_LOADED', - UpdateNetworkId = 'UPDATE_NETWORK_ID', - UpdateChosenAssetToken = 'UPDATE_CHOSEN_ASSET_TOKEN', - UpdateChosenAssetTokenAddress = 'UPDATE_CHOSEN_ASSET_TOKEN_ADDRESS', - UpdateOrderTakerAddress = 'UPDATE_ORDER_TAKER_ADDRESS', - UpdateOrderSalt = 'UPDATE_ORDER_SALT', - UpdateOrderSignatureData = 'UPDATE_ORDER_SIGNATURE_DATA', - UpdateTokenByAddress = 'UPDATE_TOKEN_BY_ADDRESS', - RemoveTokenFromTokenByAddress = 'REMOVE_TOKEN_FROM_TOKEN_BY_ADDRESS', - UpdateTokenStateByAddress = 'UPDATE_TOKEN_STATE_BY_ADDRESS', - RemoveFromTokenStateByAddress = 'REMOVE_FROM_TOKEN_STATE_BY_ADDRESS', - ReplaceTokenAllowanceByAddress = 'REPLACE_TOKEN_ALLOWANCE_BY_ADDRESS', - ReplaceTokenBalanceByAddress = 'REPLACE_TOKEN_BALANCE_BY_ADDRESS', - UpdateTokenBalanceByAddress = 'UPDATE_TOKEN_BALANCE_BY_ADDRESS', - UpdateOrderExpiry = 'UPDATE_ORDER_EXPIRY', - SwapAssetTokens = 'SWAP_ASSET_TOKENS', - UpdateUserAddress = 'UPDATE_USER_ADDRESS', - UpdateUserEtherBalance = 'UPDATE_USER_ETHER_BALANCE', - UpdateUserSuppliedOrderCache = 'UPDATE_USER_SUPPLIED_ORDER_CACHE', - UpdateOrderFillAmount = 'UPDATE_ORDER_FILL_AMOUNT', - UpdateShouldBlockchainErrDialogBeOpen = 'UPDATE_SHOULD_BLOCKCHAIN_ERR_DIALOG_BE_OPEN', - - // Docs - UpdateLibraryVersion = 'UPDATE_LIBRARY_VERSION', - UpdateAvailableLibraryVersions = 'UPDATE_AVAILABLE_LIBRARY_VERSIONS', - - // Shared - ShowFlashMessage = 'SHOW_FLASH_MESSAGE', - HideFlashMessage = 'HIDE_FLASH_MESSAGE', - UpdateProviderType = 'UPDATE_PROVIDER_TYPE', - UpdateInjectedProviderName = 'UPDATE_INJECTED_PROVIDER_NAME', + // Portal + UpdateScreenWidth = 'UPDATE_SCREEN_WIDTH', + UpdateNodeVersion = 'UPDATE_NODE_VERSION', + ResetState = 'RESET_STATE', + AddTokenToTokenByAddress = 'ADD_TOKEN_TO_TOKEN_BY_ADDRESS', + BlockchainErrEncountered = 'BLOCKCHAIN_ERR_ENCOUNTERED', + ClearTokenByAddress = 'CLEAR_TOKEN_BY_ADDRESS', + UpdateBlockchainIsLoaded = 'UPDATE_BLOCKCHAIN_IS_LOADED', + UpdateNetworkId = 'UPDATE_NETWORK_ID', + UpdateChosenAssetToken = 'UPDATE_CHOSEN_ASSET_TOKEN', + UpdateChosenAssetTokenAddress = 'UPDATE_CHOSEN_ASSET_TOKEN_ADDRESS', + UpdateOrderTakerAddress = 'UPDATE_ORDER_TAKER_ADDRESS', + UpdateOrderSalt = 'UPDATE_ORDER_SALT', + UpdateOrderSignatureData = 'UPDATE_ORDER_SIGNATURE_DATA', + UpdateTokenByAddress = 'UPDATE_TOKEN_BY_ADDRESS', + RemoveTokenFromTokenByAddress = 'REMOVE_TOKEN_FROM_TOKEN_BY_ADDRESS', + UpdateTokenStateByAddress = 'UPDATE_TOKEN_STATE_BY_ADDRESS', + RemoveFromTokenStateByAddress = 'REMOVE_FROM_TOKEN_STATE_BY_ADDRESS', + ReplaceTokenAllowanceByAddress = 'REPLACE_TOKEN_ALLOWANCE_BY_ADDRESS', + ReplaceTokenBalanceByAddress = 'REPLACE_TOKEN_BALANCE_BY_ADDRESS', + UpdateTokenBalanceByAddress = 'UPDATE_TOKEN_BALANCE_BY_ADDRESS', + UpdateOrderExpiry = 'UPDATE_ORDER_EXPIRY', + SwapAssetTokens = 'SWAP_ASSET_TOKENS', + UpdateUserAddress = 'UPDATE_USER_ADDRESS', + UpdateUserEtherBalance = 'UPDATE_USER_ETHER_BALANCE', + UpdateUserSuppliedOrderCache = 'UPDATE_USER_SUPPLIED_ORDER_CACHE', + UpdateOrderFillAmount = 'UPDATE_ORDER_FILL_AMOUNT', + UpdateShouldBlockchainErrDialogBeOpen = 'UPDATE_SHOULD_BLOCKCHAIN_ERR_DIALOG_BE_OPEN', + + // Docs + UpdateLibraryVersion = 'UPDATE_LIBRARY_VERSION', + UpdateAvailableLibraryVersions = 'UPDATE_AVAILABLE_LIBRARY_VERSIONS', + + // Shared + ShowFlashMessage = 'SHOW_FLASH_MESSAGE', + HideFlashMessage = 'HIDE_FLASH_MESSAGE', + UpdateProviderType = 'UPDATE_PROVIDER_TYPE', + UpdateInjectedProviderName = 'UPDATE_INJECTED_PROVIDER_NAME', } export interface Action { - type: ActionTypes; - data?: any; + type: ActionTypes; + data?: any; } export interface TrackedTokensByNetworkId { - [networkId: number]: Token[]; + [networkId: number]: Token[]; } export interface TrackedTokensByUserAddress { - [userAddress: string]: TrackedTokensByNetworkId; + [userAddress: string]: TrackedTokensByNetworkId; } export interface Styles { - [name: string]: React.CSSProperties; + [name: string]: React.CSSProperties; } export interface ProfileInfo { - name: string; - title?: string; - description: string; - image: string; - linkedIn?: string; - github?: string; - angellist?: string; - medium?: string; - twitter?: string; + name: string; + title?: string; + description: string; + image: string; + linkedIn?: string; + github?: string; + angellist?: string; + medium?: string; + twitter?: string; } export interface Partner { - name: string; - logo: string; - url: string; + name: string; + logo: string; + url: string; } export interface Statistic { - title: string; - figure: string; + title: string; + figure: string; } export interface StatisticByKey { - [key: string]: Statistic; + [key: string]: Statistic; } export interface ERC20MarketInfo { - etherMarketCapUsd: number; - numLiquidERC20Tokens: number; - marketCapERC20TokensUsd: number; + etherMarketCapUsd: number; + numLiquidERC20Tokens: number; + marketCapERC20TokensUsd: number; } export enum ExchangeContractErrs { - OrderFillExpired = 'ORDER_FILL_EXPIRED', - OrderAlreadyCancelledOrFilled = 'ORDER_ALREADY_CANCELLED_OR_FILLED', - OrderRemainingFillAmountZero = 'ORDER_REMAINING_FILL_AMOUNT_ZERO', - OrderFillRoundingError = 'ORDER_FILL_ROUNDING_ERROR', - FillBalanceAllowanceError = 'FILL_BALANCE_ALLOWANCE_ERROR', - InsufficientTakerBalance = 'INSUFFICIENT_TAKER_BALANCE', - InsufficientTakerAllowance = 'INSUFFICIENT_TAKER_ALLOWANCE', - InsufficientMakerBalance = 'INSUFFICIENT_MAKER_BALANCE', - InsufficientMakerAllowance = 'INSUFFICIENT_MAKER_ALLOWANCE', - TransactionSenderIsNotFillOrderTaker = 'TRANSACTION_SENDER_IS_NOT_FILL_ORDER_TAKER', - InsufficientRemainingFillAmount = 'INSUFFICIENT_REMAINING_FILL_AMOUNT', + OrderFillExpired = 'ORDER_FILL_EXPIRED', + OrderAlreadyCancelledOrFilled = 'ORDER_ALREADY_CANCELLED_OR_FILLED', + OrderRemainingFillAmountZero = 'ORDER_REMAINING_FILL_AMOUNT_ZERO', + OrderFillRoundingError = 'ORDER_FILL_ROUNDING_ERROR', + FillBalanceAllowanceError = 'FILL_BALANCE_ALLOWANCE_ERROR', + InsufficientTakerBalance = 'INSUFFICIENT_TAKER_BALANCE', + InsufficientTakerAllowance = 'INSUFFICIENT_TAKER_ALLOWANCE', + InsufficientMakerBalance = 'INSUFFICIENT_MAKER_BALANCE', + InsufficientMakerAllowance = 'INSUFFICIENT_MAKER_ALLOWANCE', + TransactionSenderIsNotFillOrderTaker = 'TRANSACTION_SENDER_IS_NOT_FILL_ORDER_TAKER', + InsufficientRemainingFillAmount = 'INSUFFICIENT_REMAINING_FILL_AMOUNT', } export interface ContractResponse { - logs: ContractEvent[]; + logs: ContractEvent[]; } export interface ContractEvent { - event: string; - args: any; + event: string; + args: any; } export type InputErrMsg = React.ReactNode | string | undefined; export type ValidatedBigNumberCallback = (isValid: boolean, amount?: BigNumber) => void; export enum ScreenWidths { - Sm = 'SM', - Md = 'MD', - Lg = 'LG', + Sm = 'SM', + Md = 'MD', + Lg = 'LG', } export enum AlertTypes { - ERROR, - SUCCESS, + ERROR, + SUCCESS, } export enum EtherscanLinkSuffixes { - Address = 'address', - Tx = 'tx', + Address = 'address', + Tx = 'tx', } export enum BlockchainErrs { - AContractNotDeployedOnNetwork = 'A_CONTRACT_NOT_DEPLOYED_ON_NETWORK', - DisconnectedFromEthereumNode = 'DISCONNECTED_FROM_ETHEREUM_NODE', - NoError = 'NO_ERROR', + AContractNotDeployedOnNetwork = 'A_CONTRACT_NOT_DEPLOYED_ON_NETWORK', + DisconnectedFromEthereumNode = 'DISCONNECTED_FROM_ETHEREUM_NODE', + NoError = 'NO_ERROR', } export enum BlockchainCallErrs { - ContractDoesNotExist = 'CONTRACT_DOES_NOT_EXIST', - UserHasNoAssociatedAddresses = 'USER_HAS_NO_ASSOCIATED_ADDRESSES', - UnhandledError = 'UNHANDLED_ERROR', - TokenAddressIsInvalid = 'TOKEN_ADDRESS_IS_INVALID', + ContractDoesNotExist = 'CONTRACT_DOES_NOT_EXIST', + UserHasNoAssociatedAddresses = 'USER_HAS_NO_ASSOCIATED_ADDRESSES', + UnhandledError = 'UNHANDLED_ERROR', + TokenAddressIsInvalid = 'TOKEN_ADDRESS_IS_INVALID', } // Exception: We don't make the values uppercase because these KindString's need to // match up those returned by TypeDoc export enum KindString { - Constructor = 'Constructor', - Property = 'Property', - Method = 'Method', - Interface = 'Interface', - TypeAlias = 'Type alias', - Variable = 'Variable', - Function = 'Function', - Enumeration = 'Enumeration', + Constructor = 'Constructor', + Property = 'Property', + Method = 'Method', + Interface = 'Interface', + TypeAlias = 'Type alias', + Variable = 'Variable', + Function = 'Function', + Enumeration = 'Enumeration', } export interface EnumValue { - name: string; - defaultValue?: string; + name: string; + defaultValue?: string; } export enum Environments { - DEVELOPMENT, - PRODUCTION, + DEVELOPMENT, + PRODUCTION, } export type ContractInstance = any; // TODO: add type definition for Contract export interface TypeDocType { - type: TypeDocTypes; - value: string; - name: string; - types: TypeDocType[]; - typeArguments?: TypeDocType[]; - declaration: TypeDocNode; - elementType?: TypeDocType; + type: TypeDocTypes; + value: string; + name: string; + types: TypeDocType[]; + typeArguments?: TypeDocType[]; + declaration: TypeDocNode; + elementType?: TypeDocType; } export interface TypeDocFlags { - isStatic?: boolean; - isOptional?: boolean; - isPublic?: boolean; + isStatic?: boolean; + isOptional?: boolean; + isPublic?: boolean; } export interface TypeDocGroup { - title: string; - children: number[]; + title: string; + children: number[]; } export interface TypeDocNode { - id?: number; - name?: string; - kind?: string; - defaultValue?: string; - kindString?: string; - type?: TypeDocType; - fileName?: string; - line?: number; - comment?: TypeDocNode; - text?: string; - shortText?: string; - returns?: string; - declaration: TypeDocNode; - flags?: TypeDocFlags; - indexSignature?: TypeDocNode[]; - signatures?: TypeDocNode[]; - parameters?: TypeDocNode[]; - typeParameter?: TypeDocNode[]; - sources?: TypeDocNode[]; - children?: TypeDocNode[]; - groups?: TypeDocGroup[]; + id?: number; + name?: string; + kind?: string; + defaultValue?: string; + kindString?: string; + type?: TypeDocType; + fileName?: string; + line?: number; + comment?: TypeDocNode; + text?: string; + shortText?: string; + returns?: string; + declaration: TypeDocNode; + flags?: TypeDocFlags; + indexSignature?: TypeDocNode[]; + signatures?: TypeDocNode[]; + parameters?: TypeDocNode[]; + typeParameter?: TypeDocNode[]; + sources?: TypeDocNode[]; + children?: TypeDocNode[]; + groups?: TypeDocGroup[]; } export enum TypeDocTypes { - Intrinsic = 'intrinsic', - Reference = 'reference', - Array = 'array', - StringLiteral = 'stringLiteral', - Reflection = 'reflection', - Union = 'union', - TypeParameter = 'typeParameter', - Unknown = 'unknown', + Intrinsic = 'intrinsic', + Reference = 'reference', + Array = 'array', + StringLiteral = 'stringLiteral', + Reflection = 'reflection', + Union = 'union', + TypeParameter = 'typeParameter', + Unknown = 'unknown', } export interface DocAgnosticFormat { - [sectionName: string]: DocSection; + [sectionName: string]: DocSection; } export interface DocSection { - comment: string; - constructors: Array; - methods: Array; - properties: Property[]; - types: CustomType[]; - events?: Event[]; + comment: string; + constructors: Array; + methods: Array; + properties: Property[]; + types: CustomType[]; + events?: Event[]; } export interface Event { - name: string; - eventArgs: EventArg[]; + name: string; + eventArgs: EventArg[]; } export interface EventArg { - isIndexed: boolean; - name: string; - type: Type; + isIndexed: boolean; + name: string; + type: Type; } export interface Property { - name: string; - type: Type; - source?: Source; - comment?: string; + name: string; + type: Type; + source?: Source; + comment?: string; } export interface BaseMethod { - isConstructor: boolean; - name: string; - returnComment?: string | undefined; - callPath: string; - parameters: Parameter[]; - returnType: Type; - comment?: string; + isConstructor: boolean; + name: string; + returnComment?: string | undefined; + callPath: string; + parameters: Parameter[]; + returnType: Type; + comment?: string; } export interface TypescriptMethod extends BaseMethod { - source?: Source; - isStatic?: boolean; - typeParameter?: TypeParameter; + source?: Source; + isStatic?: boolean; + typeParameter?: TypeParameter; } export interface SolidityMethod extends BaseMethod { - isConstant?: boolean; - isPayable?: boolean; + isConstant?: boolean; + isPayable?: boolean; } export interface Source { - fileName: string; - line: number; + fileName: string; + line: number; } export interface Parameter { - name: string; - comment: string; - isOptional: boolean; - type: Type; + name: string; + comment: string; + isOptional: boolean; + type: Type; } export interface TypeParameter { - name: string; - type: Type; + name: string; + type: Type; } export interface Type { - name: string; - typeDocType: TypeDocTypes; - value?: string; - typeArguments?: Type[]; - elementType?: ElementType; - types?: Type[]; - method?: TypescriptMethod; + name: string; + typeDocType: TypeDocTypes; + value?: string; + typeArguments?: Type[]; + elementType?: ElementType; + types?: Type[]; + method?: TypescriptMethod; } export interface ElementType { - name: string; - typeDocType: TypeDocTypes; + name: string; + typeDocType: TypeDocTypes; } export interface IndexSignature { - keyName: string; - keyType: Type; - valueName: string; + keyName: string; + keyType: Type; + valueName: string; } export interface CustomType { - name: string; - kindString: string; - type?: Type; - method?: TypescriptMethod; - indexSignature?: IndexSignature; - defaultValue?: string; - comment?: string; - children?: CustomTypeChild[]; + name: string; + kindString: string; + type?: Type; + method?: TypescriptMethod; + indexSignature?: IndexSignature; + defaultValue?: string; + comment?: string; + children?: CustomTypeChild[]; } export interface CustomTypeChild { - name: string; - type?: Type; - defaultValue?: string; + name: string; + type?: Type; + defaultValue?: string; } export interface FAQQuestion { - prompt: string; - answer: React.ReactNode; + prompt: string; + answer: React.ReactNode; } export interface FAQSection { - name: string; - questions: FAQQuestion[]; + name: string; + questions: FAQQuestion[]; } export interface S3FileObject { - Key: { - _text: string; - }; + Key: { + _text: string; + }; } export interface MenuSubsectionsBySection { - [section: string]: string[]; + [section: string]: string[]; } export enum ProviderType { - Injected = 'INJECTED', - Ledger = 'LEDGER', + Injected = 'INJECTED', + Ledger = 'LEDGER', } export interface Fact { - title: string; - explanation: string; - image: string; + title: string; + explanation: string; + image: string; } interface LedgerGetAddressResult { - address: string; + address: string; } interface LedgerSignResult { - v: string; - r: string; - s: string; + v: string; + r: string; + s: string; } interface LedgerCommunication { - close_async: () => Promise; + close_async: () => Promise; } export interface LedgerEthConnection { - getAddress_async: ( - derivationPath: string, - askForDeviceConfirmation: boolean, - shouldGetChainCode: boolean, - ) => Promise; - signPersonalMessage_async: (derivationPath: string, messageHex: string) => Promise; - signTransaction_async: (derivationPath: string, txHex: string) => Promise; - comm: LedgerCommunication; + getAddress_async: ( + derivationPath: string, + askForDeviceConfirmation: boolean, + shouldGetChainCode: boolean, + ) => Promise; + signPersonalMessage_async: (derivationPath: string, messageHex: string) => Promise; + signTransaction_async: (derivationPath: string, txHex: string) => Promise; + comm: LedgerCommunication; } export interface SignPersonalMessageParams { - data: string; + data: string; } export interface TxParams { - nonce: string; - gasPrice?: number; - gasLimit: string; - to: string; - value?: string; - data?: string; - chainId: number; // EIP 155 chainId - mainnet: 1, ropsten: 3 + nonce: string; + gasPrice?: number; + gasLimit: string; + to: string; + value?: string; + data?: string; + chainId: number; // EIP 155 chainId - mainnet: 1, ropsten: 3 } export interface PublicNodeUrlsByNetworkId { - [networkId: number]: string[]; + [networkId: number]: string[]; } export interface JSONRPCPayload { - params: any[]; - method: string; + params: any[]; + method: string; } export interface BlogPost { - image: string; - date: string; - title: string; - description: string; - url: string; + image: string; + date: string; + title: string; + description: string; + url: string; } export interface TypeDefinitionByName { - [typeName: string]: CustomType; + [typeName: string]: CustomType; } export interface Article { - section: string; - title: string; - content: string; - fileName: string; + section: string; + title: string; + content: string; + fileName: string; } export interface ArticlesBySection { - [section: string]: Article[]; + [section: string]: Article[]; } export interface DialogConfigs { - title: string; - isModal: boolean; - actions: any[]; + title: string; + isModal: boolean; + actions: any[]; } export enum TokenVisibility { - ALL = 'ALL', - UNTRACKED = 'UNTRACKED', - TRACKED = 'TRACKED', + ALL = 'ALL', + UNTRACKED = 'UNTRACKED', + TRACKED = 'TRACKED', } export enum HeaderSizes { - H1 = 'h1', - H2 = 'h2', - H3 = 'h3', + H1 = 'h1', + H2 = 'h2', + H3 = 'h3', } export interface DoxityDocObj { - [contractName: string]: DoxityContractObj; + [contractName: string]: DoxityContractObj; } export interface DoxityContractObj { - title: string; - fileName: string; - name: string; - abiDocs: DoxityAbiDoc[]; + title: string; + fileName: string; + name: string; + abiDocs: DoxityAbiDoc[]; } export interface DoxityAbiDoc { - constant: boolean; - inputs: DoxityInput[]; - name: string; - outputs: DoxityOutput[]; - payable: boolean; - type: string; - details?: string; - return?: string; + constant: boolean; + inputs: DoxityInput[]; + name: string; + outputs: DoxityOutput[]; + payable: boolean; + type: string; + details?: string; + return?: string; } export interface DoxityOutput { - name: string; - type: string; + name: string; + type: string; } export interface DoxityInput { - name: string; - type: string; - description: string; - indexed?: boolean; + name: string; + type: string; + description: string; + indexed?: boolean; } export interface VersionToFileName { - [version: string]: string; + [version: string]: string; } export enum Docs { - ZeroExJs, - SmartContracts, + ZeroExJs, + SmartContracts, } export interface ContractAddresses { - [version: string]: { - [network: string]: AddressByContractName; - }; + [version: string]: { + [network: string]: AddressByContractName; + }; } export interface AddressByContractName { - [contractName: string]: string; + [contractName: string]: string; } export enum Networks { - mainnet = 'Mainnet', - kovan = 'Kovan', - ropsten = 'Ropsten', - rinkeby = 'Rinkeby', + mainnet = 'Mainnet', + kovan = 'Kovan', + ropsten = 'Ropsten', + rinkeby = 'Rinkeby', } export enum AbiTypes { - Constructor = 'constructor', - Function = 'function', - Event = 'event', + Constructor = 'constructor', + Function = 'function', + Event = 'event', } export enum WebsitePaths { - Portal = '/portal', - Wiki = '/wiki', - ZeroExJs = '/docs/0xjs', - Home = '/', - FAQ = '/faq', - About = '/about', - Whitepaper = '/pdfs/0x_white_paper.pdf', - SmartContracts = '/docs/contracts', - Connect = '/docs/connect', + Portal = '/portal', + Wiki = '/wiki', + ZeroExJs = '/docs/0xjs', + Home = '/', + FAQ = '/faq', + About = '/about', + Whitepaper = '/pdfs/0x_white_paper.pdf', + SmartContracts = '/docs/contracts', + Connect = '/docs/connect', } export interface DocsMenu { - [sectionName: string]: string[]; + [sectionName: string]: string[]; } export interface SectionsMap { - [sectionName: string]: string; + [sectionName: string]: string; } export interface DocsInfoConfig { - displayName: string; - packageUrl: string; - websitePath: string; - docsJsonRoot: string; - menu: DocsMenu; - sections: SectionsMap; - sectionNameToMarkdown: { [sectionName: string]: string }; - visibleConstructors: string[]; - convertToDocAgnosticFormatFn: (docObj: DoxityDocObj | TypeDocNode, docsInfo?: any) => DocAgnosticFormat; - subPackageName?: string; - publicTypes?: string[]; - sectionNameToModulePath?: { [sectionName: string]: string[] }; - menuSubsectionToVersionWhenIntroduced?: { [sectionName: string]: string }; + displayName: string; + packageUrl: string; + websitePath: string; + docsJsonRoot: string; + menu: DocsMenu; + sections: SectionsMap; + sectionNameToMarkdown: { [sectionName: string]: string }; + visibleConstructors: string[]; + convertToDocAgnosticFormatFn: (docObj: DoxityDocObj | TypeDocNode, docsInfo?: any) => DocAgnosticFormat; + subPackageName?: string; + publicTypes?: string[]; + sectionNameToModulePath?: { [sectionName: string]: string[] }; + menuSubsectionToVersionWhenIntroduced?: { [sectionName: string]: string }; } export interface TimestampMsRange { - startTimestampMs: number; - endTimestampMs: number; + startTimestampMs: number; + endTimestampMs: number; } export interface OutdatedWrappedEtherByNetworkId { - [networkId: number]: { - address: string; - timestampMsRange: TimestampMsRange; - }; + [networkId: number]: { + address: string; + timestampMsRange: TimestampMsRange; + }; } export enum SmartContractDocSections { - Introduction = 'Introduction', - Exchange = 'Exchange', - TokenTransferProxy = 'TokenTransferProxy', - TokenRegistry = 'TokenRegistry', - ZRXToken = 'ZRXToken', + Introduction = 'Introduction', + Exchange = 'Exchange', + TokenTransferProxy = 'TokenTransferProxy', + TokenRegistry = 'TokenRegistry', + ZRXToken = 'ZRXToken', } // tslint:disable:max-file-line-count diff --git a/packages/website/ts/utils/colors.ts b/packages/website/ts/utils/colors.ts index dabc1fd54..58ce667e3 100644 --- a/packages/website/ts/utils/colors.ts +++ b/packages/website/ts/utils/colors.ts @@ -1,43 +1,43 @@ import { colors as materialUiColors } from 'material-ui/styles'; export const colors = { - ...materialUiColors, - grey50: '#FAFAFA', - grey100: '#F5F5F5', - lightestGrey: '#F0F0F0', - greyishPink: '#E6E5E5', - grey300: '#E0E0E0', - beigeWhite: '#E4E4E4', - grey400: '#BDBDBD', - lightGrey: '#BBBBBB', - grey500: '#9E9E9E', - grey: '#A5A5A5', - darkGrey: '#818181', - landingLinkGrey: '#919191', - grey700: '#616161', - grey800: '#424242', - darkerGrey: '#393939', - heroGrey: '#404040', - projectsGrey: '#343333', - darkestGrey: '#272727', - dharmaDarkGrey: '#252525', - lightBlue: '#60A4F4', - lightBlueA700: '#0091EA', - darkBlue: '#4D5481', - turquois: '#058789', - lightPurple: '#A81CA6', - purple: '#690596', - red200: '#EF9A9A', - red: '#E91751', - red500: '#F44336', - red600: '#E53935', - limeGreen: '#66DE75', - lightGreen: '#4DC55C', - lightestGreen: '#89C774', - brightGreen: '#00C33E', - green400: '#66BB6A', - green: '#4DA24B', - amber600: '#FFB300', - orange: '#E69D00', - amber800: '#FF8F00', + ...materialUiColors, + grey50: '#FAFAFA', + grey100: '#F5F5F5', + lightestGrey: '#F0F0F0', + greyishPink: '#E6E5E5', + grey300: '#E0E0E0', + beigeWhite: '#E4E4E4', + grey400: '#BDBDBD', + lightGrey: '#BBBBBB', + grey500: '#9E9E9E', + grey: '#A5A5A5', + darkGrey: '#818181', + landingLinkGrey: '#919191', + grey700: '#616161', + grey800: '#424242', + darkerGrey: '#393939', + heroGrey: '#404040', + projectsGrey: '#343333', + darkestGrey: '#272727', + dharmaDarkGrey: '#252525', + lightBlue: '#60A4F4', + lightBlueA700: '#0091EA', + darkBlue: '#4D5481', + turquois: '#058789', + lightPurple: '#A81CA6', + purple: '#690596', + red200: '#EF9A9A', + red: '#E91751', + red500: '#F44336', + red600: '#E53935', + limeGreen: '#66DE75', + lightGreen: '#4DC55C', + lightestGreen: '#89C774', + brightGreen: '#00C33E', + green400: '#66BB6A', + green: '#4DA24B', + amber600: '#FFB300', + orange: '#E69D00', + amber800: '#FF8F00', }; diff --git a/packages/website/ts/utils/configs.ts b/packages/website/ts/utils/configs.ts index 6327ea05a..3d37a89ab 100644 --- a/packages/website/ts/utils/configs.ts +++ b/packages/website/ts/utils/configs.ts @@ -1,126 +1,126 @@ import * as _ from 'lodash'; import { - ContractAddresses, - Environments, - Networks, - OutdatedWrappedEtherByNetworkId, - PublicNodeUrlsByNetworkId, - SmartContractDocSections, + ContractAddresses, + Environments, + Networks, + OutdatedWrappedEtherByNetworkId, + PublicNodeUrlsByNetworkId, + SmartContractDocSections, } from 'ts/types'; const BASE_URL = window.location.origin; const isDevelopment = _.includes( - ['https://0xproject.localhost:3572', 'https://localhost:3572', 'https://127.0.0.1'], - BASE_URL, + ['https://0xproject.localhost:3572', 'https://localhost:3572', 'https://127.0.0.1'], + BASE_URL, ); const INFURA_API_KEY = 'T5WSC8cautR4KXyYgsRs'; export const configs = { - BACKEND_BASE_URL: isDevelopment ? 'https://localhost:3001' : 'https://website-api.0xproject.com', - BASE_URL, - BITLY_ACCESS_TOKEN: 'ffc4c1a31e5143848fb7c523b39f91b9b213d208', - CONTRACT_ADDRESS: { - '1.0.0': { - [Networks.mainnet]: { - [SmartContractDocSections.Exchange]: '0x12459c951127e0c374ff9105dda097662a027093', - [SmartContractDocSections.TokenTransferProxy]: '0x8da0d80f5007ef1e431dd2127178d224e32c2ef4', - [SmartContractDocSections.ZRXToken]: '0xe41d2489571d322189246dafa5ebde1f4699f498', - [SmartContractDocSections.TokenRegistry]: '0x926a74c5c36adf004c87399e65f75628b0f98d2c', - }, - [Networks.ropsten]: { - [SmartContractDocSections.Exchange]: '0x479cc461fecd078f766ecc58533d6f69580cf3ac', - [SmartContractDocSections.TokenTransferProxy]: '0x4e9aad8184de8833365fea970cd9149372fdf1e6', - [SmartContractDocSections.ZRXToken]: '0xa8e9fa8f91e5ae138c74648c9c304f1c75003a8d', - [SmartContractDocSections.TokenRegistry]: '0x6b1a50f0bb5a7995444bd3877b22dc89c62843ed', - }, - [Networks.kovan]: { - [SmartContractDocSections.Exchange]: '0x90fe2af704b34e0224bf2299c838e04d4dcf1364', - [SmartContractDocSections.TokenTransferProxy]: '0x087Eed4Bc1ee3DE49BeFbd66C662B434B15d49d4', - [SmartContractDocSections.ZRXToken]: '0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570', - [SmartContractDocSections.TokenRegistry]: '0xf18e504561f4347bea557f3d4558f559dddbae7f', - }, - }, - } as ContractAddresses, - DEFAULT_DERIVATION_PATH: `44'/60'/0'`, - // WARNING: ZRX & WETH MUST always be default trackedTokens - DEFAULT_TRACKED_TOKEN_SYMBOLS: ['WETH', 'ZRX'], - DOMAIN_STAGING: 'staging-0xproject.s3-website-us-east-1.amazonaws.com', - DOMAIN_DEVELOPMENT: '0xproject.localhost:3572', - DOMAIN_PRODUCTION: '0xproject.com', - ENVIRONMENT: isDevelopment ? Environments.DEVELOPMENT : Environments.PRODUCTION, - ICON_URL_BY_SYMBOL: { - REP: '/images/token_icons/augur.png', - DGD: '/images/token_icons/digixdao.png', - WETH: '/images/token_icons/ether_erc20.png', - MLN: '/images/token_icons/melon.png', - GNT: '/images/token_icons/golem.png', - MKR: '/images/token_icons/makerdao.png', - ZRX: '/images/token_icons/zero_ex.png', - ANT: '/images/token_icons/aragon.png', - BNT: '/images/token_icons/bancor.png', - BAT: '/images/token_icons/basicattentiontoken.png', - CVC: '/images/token_icons/civic.png', - EOS: '/images/token_icons/eos.png', - FUN: '/images/token_icons/funfair.png', - GNO: '/images/token_icons/gnosis.png', - ICN: '/images/token_icons/iconomi.png', - OMG: '/images/token_icons/omisego.png', - SNT: '/images/token_icons/status.png', - STORJ: '/images/token_icons/storjcoinx.png', - PAY: '/images/token_icons/tenx.png', - QTUM: '/images/token_icons/qtum.png', - DNT: '/images/token_icons/district0x.png', - SNGLS: '/images/token_icons/singularity.png', - EDG: '/images/token_icons/edgeless.png', - '1ST': '/images/token_icons/firstblood.jpg', - WINGS: '/images/token_icons/wings.png', - BQX: '/images/token_icons/bitquence.png', - LUN: '/images/token_icons/lunyr.png', - RLC: '/images/token_icons/iexec.png', - MCO: '/images/token_icons/monaco.png', - ADT: '/images/token_icons/adtoken.png', - CFI: '/images/token_icons/cofound-it.png', - ROL: '/images/token_icons/etheroll.png', - WGNT: '/images/token_icons/golem.png', - MTL: '/images/token_icons/metal.png', - NMR: '/images/token_icons/numeraire.png', - SAN: '/images/token_icons/santiment.png', - TAAS: '/images/token_icons/taas.png', - TKN: '/images/token_icons/tokencard.png', - TRST: '/images/token_icons/trust.png', - } as { [symbol: string]: string }, - IS_MAINNET_ENABLED: true, - LAST_LOCAL_STORAGE_FILL_CLEARANCE_DATE: '2017-11-22', - LAST_LOCAL_STORAGE_TRACKED_TOKEN_CLEARANCE_DATE: '2017-12-19', - // NEW_WRAPPED_ETHERS is temporary until we remove the SHOULD_DEPRECATE_OLD_WETH_TOKEN flag - // and add the new WETHs to the tokenRegistry - NEW_WRAPPED_ETHERS: { - 1: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - 42: '0xd0a1e359811322d97991e03f863a0c30c2cf029c', - } as { [networkId: string]: string }, - OUTDATED_WRAPPED_ETHERS: [ - { - 42: { - address: '0x05d090b51c40b020eab3bfcb6a2dff130df22e9c', - timestampMsRange: { - startTimestampMs: 1502455607000, - endTimestampMs: 1513790926000, - }, - }, - 1: { - address: '0x2956356cd2a2bf3202f771f50d3d14a367b48070', - timestampMsRange: { - startTimestampMs: 1502455607000, - endTimestampMs: 1513790926000, - }, - }, - }, - ] as OutdatedWrappedEtherByNetworkId[], - // The order matters. We first try first node and only then fall back to others. - PUBLIC_NODE_URLS_BY_NETWORK_ID: { - [1]: [`https://mainnet.infura.io/${INFURA_API_KEY}`, 'https://mainnet.0xproject.com'], - [42]: [`https://kovan.infura.io/${INFURA_API_KEY}`, 'https://kovan.0xproject.com'], - } as PublicNodeUrlsByNetworkId, - SHOULD_DEPRECATE_OLD_WETH_TOKEN: true, - SYMBOLS_OF_MINTABLE_TOKENS: ['MKR', 'MLN', 'GNT', 'DGD', 'REP'], + BACKEND_BASE_URL: isDevelopment ? 'https://localhost:3001' : 'https://website-api.0xproject.com', + BASE_URL, + BITLY_ACCESS_TOKEN: 'ffc4c1a31e5143848fb7c523b39f91b9b213d208', + CONTRACT_ADDRESS: { + '1.0.0': { + [Networks.mainnet]: { + [SmartContractDocSections.Exchange]: '0x12459c951127e0c374ff9105dda097662a027093', + [SmartContractDocSections.TokenTransferProxy]: '0x8da0d80f5007ef1e431dd2127178d224e32c2ef4', + [SmartContractDocSections.ZRXToken]: '0xe41d2489571d322189246dafa5ebde1f4699f498', + [SmartContractDocSections.TokenRegistry]: '0x926a74c5c36adf004c87399e65f75628b0f98d2c', + }, + [Networks.ropsten]: { + [SmartContractDocSections.Exchange]: '0x479cc461fecd078f766ecc58533d6f69580cf3ac', + [SmartContractDocSections.TokenTransferProxy]: '0x4e9aad8184de8833365fea970cd9149372fdf1e6', + [SmartContractDocSections.ZRXToken]: '0xa8e9fa8f91e5ae138c74648c9c304f1c75003a8d', + [SmartContractDocSections.TokenRegistry]: '0x6b1a50f0bb5a7995444bd3877b22dc89c62843ed', + }, + [Networks.kovan]: { + [SmartContractDocSections.Exchange]: '0x90fe2af704b34e0224bf2299c838e04d4dcf1364', + [SmartContractDocSections.TokenTransferProxy]: '0x087Eed4Bc1ee3DE49BeFbd66C662B434B15d49d4', + [SmartContractDocSections.ZRXToken]: '0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570', + [SmartContractDocSections.TokenRegistry]: '0xf18e504561f4347bea557f3d4558f559dddbae7f', + }, + }, + } as ContractAddresses, + DEFAULT_DERIVATION_PATH: `44'/60'/0'`, + // WARNING: ZRX & WETH MUST always be default trackedTokens + DEFAULT_TRACKED_TOKEN_SYMBOLS: ['WETH', 'ZRX'], + DOMAIN_STAGING: 'staging-0xproject.s3-website-us-east-1.amazonaws.com', + DOMAIN_DEVELOPMENT: '0xproject.localhost:3572', + DOMAIN_PRODUCTION: '0xproject.com', + ENVIRONMENT: isDevelopment ? Environments.DEVELOPMENT : Environments.PRODUCTION, + ICON_URL_BY_SYMBOL: { + REP: '/images/token_icons/augur.png', + DGD: '/images/token_icons/digixdao.png', + WETH: '/images/token_icons/ether_erc20.png', + MLN: '/images/token_icons/melon.png', + GNT: '/images/token_icons/golem.png', + MKR: '/images/token_icons/makerdao.png', + ZRX: '/images/token_icons/zero_ex.png', + ANT: '/images/token_icons/aragon.png', + BNT: '/images/token_icons/bancor.png', + BAT: '/images/token_icons/basicattentiontoken.png', + CVC: '/images/token_icons/civic.png', + EOS: '/images/token_icons/eos.png', + FUN: '/images/token_icons/funfair.png', + GNO: '/images/token_icons/gnosis.png', + ICN: '/images/token_icons/iconomi.png', + OMG: '/images/token_icons/omisego.png', + SNT: '/images/token_icons/status.png', + STORJ: '/images/token_icons/storjcoinx.png', + PAY: '/images/token_icons/tenx.png', + QTUM: '/images/token_icons/qtum.png', + DNT: '/images/token_icons/district0x.png', + SNGLS: '/images/token_icons/singularity.png', + EDG: '/images/token_icons/edgeless.png', + '1ST': '/images/token_icons/firstblood.jpg', + WINGS: '/images/token_icons/wings.png', + BQX: '/images/token_icons/bitquence.png', + LUN: '/images/token_icons/lunyr.png', + RLC: '/images/token_icons/iexec.png', + MCO: '/images/token_icons/monaco.png', + ADT: '/images/token_icons/adtoken.png', + CFI: '/images/token_icons/cofound-it.png', + ROL: '/images/token_icons/etheroll.png', + WGNT: '/images/token_icons/golem.png', + MTL: '/images/token_icons/metal.png', + NMR: '/images/token_icons/numeraire.png', + SAN: '/images/token_icons/santiment.png', + TAAS: '/images/token_icons/taas.png', + TKN: '/images/token_icons/tokencard.png', + TRST: '/images/token_icons/trust.png', + } as { [symbol: string]: string }, + IS_MAINNET_ENABLED: true, + LAST_LOCAL_STORAGE_FILL_CLEARANCE_DATE: '2017-11-22', + LAST_LOCAL_STORAGE_TRACKED_TOKEN_CLEARANCE_DATE: '2017-12-19', + // NEW_WRAPPED_ETHERS is temporary until we remove the SHOULD_DEPRECATE_OLD_WETH_TOKEN flag + // and add the new WETHs to the tokenRegistry + NEW_WRAPPED_ETHERS: { + 1: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + 42: '0xd0a1e359811322d97991e03f863a0c30c2cf029c', + } as { [networkId: string]: string }, + OUTDATED_WRAPPED_ETHERS: [ + { + 42: { + address: '0x05d090b51c40b020eab3bfcb6a2dff130df22e9c', + timestampMsRange: { + startTimestampMs: 1502455607000, + endTimestampMs: 1513790926000, + }, + }, + 1: { + address: '0x2956356cd2a2bf3202f771f50d3d14a367b48070', + timestampMsRange: { + startTimestampMs: 1502455607000, + endTimestampMs: 1513790926000, + }, + }, + }, + ] as OutdatedWrappedEtherByNetworkId[], + // The order matters. We first try first node and only then fall back to others. + PUBLIC_NODE_URLS_BY_NETWORK_ID: { + [1]: [`https://mainnet.infura.io/${INFURA_API_KEY}`, 'https://mainnet.0xproject.com'], + [42]: [`https://kovan.infura.io/${INFURA_API_KEY}`, 'https://kovan.0xproject.com'], + } as PublicNodeUrlsByNetworkId, + SHOULD_DEPRECATE_OLD_WETH_TOKEN: true, + SYMBOLS_OF_MINTABLE_TOKENS: ['MKR', 'MLN', 'GNT', 'DGD', 'REP'], }; diff --git a/packages/website/ts/utils/constants.ts b/packages/website/ts/utils/constants.ts index bb6407ec5..dded82114 100644 --- a/packages/website/ts/utils/constants.ts +++ b/packages/website/ts/utils/constants.ts @@ -2,86 +2,86 @@ import { BigNumber } from '@0xproject/utils'; import { Networks } from 'ts/types'; export const constants = { - DECIMAL_PLACES_ETH: 18, - DECIMAL_PLACES_ZRX: 18, - DOCS_SCROLL_DURATION_MS: 0, - DOCS_CONTAINER_ID: 'documentation', - GENESIS_ORDER_BLOCK_BY_NETWORK_ID: { - 1: 4145578, - 42: 3117574, - 50: 0, - } as { [networkId: number]: number }, - HOME_SCROLL_DURATION_MS: 500, - HTTP_NO_CONTENT_STATUS_CODE: 204, - LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER: 'didAcceptPortalDisclaimer', - LOCAL_STORAGE_KEY_DISMISS_WETH_NOTICE: 'hasDismissedWethNotice', - MAKER_FEE: new BigNumber(0), - MAINNET_NAME: 'Main network', - MINT_AMOUNT: new BigNumber('100000000000000000000'), - NETWORK_ID_MAINNET: 1, - NETWORK_ID_TESTNET: 42, - NETWORK_ID_TESTRPC: 50, - NETWORK_NAME_BY_ID: { - 1: Networks.mainnet, - 3: Networks.ropsten, - 4: Networks.rinkeby, - 42: Networks.kovan, - } as { [symbol: number]: string }, - NETWORK_ID_BY_NAME: { - [Networks.mainnet]: 1, - [Networks.ropsten]: 3, - [Networks.rinkeby]: 4, - [Networks.kovan]: 42, - } as { [networkName: string]: number }, - NULL_ADDRESS: '0x0000000000000000000000000000000000000000', - PROVIDER_NAME_LEDGER: 'Ledger', - PROVIDER_NAME_METAMASK: 'Metamask', - PROVIDER_NAME_PARITY_SIGNER: 'Parity Signer', - PROVIDER_NAME_GENERIC: 'Injected Web3', - PROVIDER_NAME_PUBLIC: '0x Public', - ROLLBAR_ACCESS_TOKEN: 'a6619002b51c4464928201e6ea94de65', - SUCCESS_STATUS: 200, - UNAVAILABLE_STATUS: 503, - TAKER_FEE: new BigNumber(0), - TESTNET_NAME: 'Kovan', - TYPES_SECTION_NAME: 'types', - PROJECT_URL_ETHFINEX: 'https://www.bitfinex.com/ethfinex', - PROJECT_URL_RADAR_RELAY: 'https://radarrelay.com', - PROJECT_URL_PARADEX: 'https://paradex.io', - PROJECT_URL_DYDX: 'https://dydx.exchange', - PROJECT_URL_MELONPORT: 'https://melonport.com', - PROJECT_URL_DISTRICT_0X: 'https://district0x.io', - PROJECT_URL_DHARMA: 'https://dharma.io', - PROJECT_URL_LENDROID: 'https://lendroid.com', - PROJECT_URL_MAKER: 'https://makerdao.com', - PROJECT_URL_ARAGON: 'https://aragon.one', - PROJECT_URL_BLOCKNET: 'https://blocknet.co', - PROJECT_URL_0CEAN: 'http://the0cean.com', - PROJECT_URL_STATUS: 'https://status.im', - PROJECT_URL_AUGUR: 'https://augur.net', - PROJECT_URL_AUCTUS: 'https://auctus.org', - PROJECT_URL_OPEN_ANX: 'https://www.openanx.org', - URL_ANGELLIST: 'https://angel.co/0xproject/jobs', - URL_BIGNUMBERJS_GITHUB: 'http://mikemcl.github.io/bignumber.js', - URL_BITLY_API: 'https://api-ssl.bitly.com', - URL_BLOG: 'https://blog.0xproject.com/latest', - URL_DISCOURSE_FORUM: 'https://forum.0xproject.com', - URL_FIREFOX_U2F_ADDON: 'https://addons.mozilla.org/en-US/firefox/addon/u2f-support-add-on/', - URL_ETHER_FAUCET: 'https://faucet.0xproject.com', - URL_GITHUB_ORG: 'https://github.com/0xProject', - URL_GITHUB_WIKI: 'https://github.com/0xProject/wiki', - URL_METAMASK_CHROME_STORE: 'https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn', - URL_MIST_DOWNLOAD: 'https://github.com/ethereum/mist/releases', - URL_PARITY_CHROME_STORE: - 'https://chrome.google.com/webstore/detail/parity-ethereum-integrati/himekenlppkgeaoeddcliojfddemadig', - URL_REDDIT: 'https://reddit.com/r/0xproject', - URL_STANDARD_RELAYER_API_GITHUB: 'https://github.com/0xProject/standard-relayer-api/blob/master/README.md', - URL_TWITTER: 'https://twitter.com/0xproject', - URL_WEB3_DOCS: 'https://github.com/ethereum/wiki/wiki/JavaScript-API', - URL_WEB3_DECODED_LOG_ENTRY_EVENT: - 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L123', - URL_WEB3_LOG_ENTRY_EVENT: 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L127', - URL_WEB3_PROVIDER_DOCS: 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L150', - URL_WETH_IO: 'https://weth.io/', - URL_ZEROEX_CHAT: 'https://chat.0xproject.com', + DECIMAL_PLACES_ETH: 18, + DECIMAL_PLACES_ZRX: 18, + DOCS_SCROLL_DURATION_MS: 0, + DOCS_CONTAINER_ID: 'documentation', + GENESIS_ORDER_BLOCK_BY_NETWORK_ID: { + 1: 4145578, + 42: 3117574, + 50: 0, + } as { [networkId: number]: number }, + HOME_SCROLL_DURATION_MS: 500, + HTTP_NO_CONTENT_STATUS_CODE: 204, + LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER: 'didAcceptPortalDisclaimer', + LOCAL_STORAGE_KEY_DISMISS_WETH_NOTICE: 'hasDismissedWethNotice', + MAKER_FEE: new BigNumber(0), + MAINNET_NAME: 'Main network', + MINT_AMOUNT: new BigNumber('100000000000000000000'), + NETWORK_ID_MAINNET: 1, + NETWORK_ID_TESTNET: 42, + NETWORK_ID_TESTRPC: 50, + NETWORK_NAME_BY_ID: { + 1: Networks.mainnet, + 3: Networks.ropsten, + 4: Networks.rinkeby, + 42: Networks.kovan, + } as { [symbol: number]: string }, + NETWORK_ID_BY_NAME: { + [Networks.mainnet]: 1, + [Networks.ropsten]: 3, + [Networks.rinkeby]: 4, + [Networks.kovan]: 42, + } as { [networkName: string]: number }, + NULL_ADDRESS: '0x0000000000000000000000000000000000000000', + PROVIDER_NAME_LEDGER: 'Ledger', + PROVIDER_NAME_METAMASK: 'Metamask', + PROVIDER_NAME_PARITY_SIGNER: 'Parity Signer', + PROVIDER_NAME_GENERIC: 'Injected Web3', + PROVIDER_NAME_PUBLIC: '0x Public', + ROLLBAR_ACCESS_TOKEN: 'a6619002b51c4464928201e6ea94de65', + SUCCESS_STATUS: 200, + UNAVAILABLE_STATUS: 503, + TAKER_FEE: new BigNumber(0), + TESTNET_NAME: 'Kovan', + TYPES_SECTION_NAME: 'types', + PROJECT_URL_ETHFINEX: 'https://www.bitfinex.com/ethfinex', + PROJECT_URL_RADAR_RELAY: 'https://radarrelay.com', + PROJECT_URL_PARADEX: 'https://paradex.io', + PROJECT_URL_DYDX: 'https://dydx.exchange', + PROJECT_URL_MELONPORT: 'https://melonport.com', + PROJECT_URL_DISTRICT_0X: 'https://district0x.io', + PROJECT_URL_DHARMA: 'https://dharma.io', + PROJECT_URL_LENDROID: 'https://lendroid.com', + PROJECT_URL_MAKER: 'https://makerdao.com', + PROJECT_URL_ARAGON: 'https://aragon.one', + PROJECT_URL_BLOCKNET: 'https://blocknet.co', + PROJECT_URL_0CEAN: 'http://the0cean.com', + PROJECT_URL_STATUS: 'https://status.im', + PROJECT_URL_AUGUR: 'https://augur.net', + PROJECT_URL_AUCTUS: 'https://auctus.org', + PROJECT_URL_OPEN_ANX: 'https://www.openanx.org', + URL_ANGELLIST: 'https://angel.co/0xproject/jobs', + URL_BIGNUMBERJS_GITHUB: 'http://mikemcl.github.io/bignumber.js', + URL_BITLY_API: 'https://api-ssl.bitly.com', + URL_BLOG: 'https://blog.0xproject.com/latest', + URL_DISCOURSE_FORUM: 'https://forum.0xproject.com', + URL_FIREFOX_U2F_ADDON: 'https://addons.mozilla.org/en-US/firefox/addon/u2f-support-add-on/', + URL_ETHER_FAUCET: 'https://faucet.0xproject.com', + URL_GITHUB_ORG: 'https://github.com/0xProject', + URL_GITHUB_WIKI: 'https://github.com/0xProject/wiki', + URL_METAMASK_CHROME_STORE: 'https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn', + URL_MIST_DOWNLOAD: 'https://github.com/ethereum/mist/releases', + URL_PARITY_CHROME_STORE: + 'https://chrome.google.com/webstore/detail/parity-ethereum-integrati/himekenlppkgeaoeddcliojfddemadig', + URL_REDDIT: 'https://reddit.com/r/0xproject', + URL_STANDARD_RELAYER_API_GITHUB: 'https://github.com/0xProject/standard-relayer-api/blob/master/README.md', + URL_TWITTER: 'https://twitter.com/0xproject', + URL_WEB3_DOCS: 'https://github.com/ethereum/wiki/wiki/JavaScript-API', + URL_WEB3_DECODED_LOG_ENTRY_EVENT: + 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L123', + URL_WEB3_LOG_ENTRY_EVENT: 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L127', + URL_WEB3_PROVIDER_DOCS: 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L150', + URL_WETH_IO: 'https://weth.io/', + URL_ZEROEX_CHAT: 'https://chat.0xproject.com', }; diff --git a/packages/website/ts/utils/doc_utils.ts b/packages/website/ts/utils/doc_utils.ts index 18bf276b4..1f5f75ee2 100644 --- a/packages/website/ts/utils/doc_utils.ts +++ b/packages/website/ts/utils/doc_utils.ts @@ -5,47 +5,47 @@ import { utils } from 'ts/utils/utils'; import convert = require('xml-js'); export const docUtils = { - async getVersionToFileNameAsync(s3DocJsonRoot: string): Promise { - const versionFileNames = await this.getVersionFileNamesAsync(s3DocJsonRoot); - const versionToFileName: VersionToFileName = {}; - _.each(versionFileNames, fileName => { - const [version] = findVersions(fileName); - versionToFileName[version] = fileName; - }); - return versionToFileName; - }, - async getVersionFileNamesAsync(s3DocJsonRoot: string): Promise { - const response = await fetch(s3DocJsonRoot); - if (response.status !== 200) { - // TODO: Show the user an error message when the docs fail to load - const errMsg = await response.text(); - utils.consoleLog(`Failed to load JSON file list: ${response.status} ${errMsg}`); - throw new Error(errMsg); - } - const responseXML = await response.text(); - const responseJSONString = convert.xml2json(responseXML, { - compact: true, - }); - const responseObj = JSON.parse(responseJSONString); - const fileObjs: S3FileObject[] = _.isArray(responseObj.ListBucketResult.Contents) - ? (responseObj.ListBucketResult.Contents as S3FileObject[]) - : [responseObj.ListBucketResult.Contents]; + async getVersionToFileNameAsync(s3DocJsonRoot: string): Promise { + const versionFileNames = await this.getVersionFileNamesAsync(s3DocJsonRoot); + const versionToFileName: VersionToFileName = {}; + _.each(versionFileNames, fileName => { + const [version] = findVersions(fileName); + versionToFileName[version] = fileName; + }); + return versionToFileName; + }, + async getVersionFileNamesAsync(s3DocJsonRoot: string): Promise { + const response = await fetch(s3DocJsonRoot); + if (response.status !== 200) { + // TODO: Show the user an error message when the docs fail to load + const errMsg = await response.text(); + utils.consoleLog(`Failed to load JSON file list: ${response.status} ${errMsg}`); + throw new Error(errMsg); + } + const responseXML = await response.text(); + const responseJSONString = convert.xml2json(responseXML, { + compact: true, + }); + const responseObj = JSON.parse(responseJSONString); + const fileObjs: S3FileObject[] = _.isArray(responseObj.ListBucketResult.Contents) + ? (responseObj.ListBucketResult.Contents as S3FileObject[]) + : [responseObj.ListBucketResult.Contents]; - const versionFileNames = _.map(fileObjs, fileObj => { - return fileObj.Key._text; - }); - return versionFileNames; - }, - async getJSONDocFileAsync(fileName: string, s3DocJsonRoot: string): Promise { - const endpoint = `${s3DocJsonRoot}/${fileName}`; - const response = await fetch(endpoint); - if (response.status !== 200) { - // TODO: Show the user an error message when the docs fail to load - const errMsg = await response.text(); - utils.consoleLog(`Failed to load Doc JSON: ${response.status} ${errMsg}`); - throw new Error(errMsg); - } - const jsonDocObj = await response.json(); - return jsonDocObj; - }, + const versionFileNames = _.map(fileObjs, fileObj => { + return fileObj.Key._text; + }); + return versionFileNames; + }, + async getJSONDocFileAsync(fileName: string, s3DocJsonRoot: string): Promise { + const endpoint = `${s3DocJsonRoot}/${fileName}`; + const response = await fetch(endpoint); + if (response.status !== 200) { + // TODO: Show the user an error message when the docs fail to load + const errMsg = await response.text(); + utils.consoleLog(`Failed to load Doc JSON: ${response.status} ${errMsg}`); + throw new Error(errMsg); + } + const jsonDocObj = await response.json(); + return jsonDocObj; + }, }; diff --git a/packages/website/ts/utils/doxity_utils.ts b/packages/website/ts/utils/doxity_utils.ts index f26534d51..5f1d02132 100644 --- a/packages/website/ts/utils/doxity_utils.ts +++ b/packages/website/ts/utils/doxity_utils.ts @@ -1,168 +1,168 @@ import * as _ from 'lodash'; import { - AbiTypes, - DocAgnosticFormat, - DocSection, - DoxityAbiDoc, - DoxityContractObj, - DoxityDocObj, - DoxityInput, - EventArg, - Parameter, - Property, - SolidityMethod, - Type, - TypeDocTypes, + AbiTypes, + DocAgnosticFormat, + DocSection, + DoxityAbiDoc, + DoxityContractObj, + DoxityDocObj, + DoxityInput, + EventArg, + Parameter, + Property, + SolidityMethod, + Type, + TypeDocTypes, } from 'ts/types'; export const doxityUtils = { - convertToDocAgnosticFormat(doxityDocObj: DoxityDocObj): DocAgnosticFormat { - const docAgnosticFormat: DocAgnosticFormat = {}; - _.each(doxityDocObj, (doxityContractObj: DoxityContractObj, contractName: string) => { - const doxityConstructor = _.find(doxityContractObj.abiDocs, (abiDoc: DoxityAbiDoc) => { - return abiDoc.type === AbiTypes.Constructor; - }); - const constructors = []; - if (!_.isUndefined(doxityConstructor)) { - const constructor = { - isConstructor: true, - name: doxityContractObj.name, - comment: doxityConstructor.details, - returnComment: doxityConstructor.return, - callPath: '', - parameters: this._convertParameters(doxityConstructor.inputs), - returnType: this._convertType(doxityContractObj.name), - }; - constructors.push(constructor); - } + convertToDocAgnosticFormat(doxityDocObj: DoxityDocObj): DocAgnosticFormat { + const docAgnosticFormat: DocAgnosticFormat = {}; + _.each(doxityDocObj, (doxityContractObj: DoxityContractObj, contractName: string) => { + const doxityConstructor = _.find(doxityContractObj.abiDocs, (abiDoc: DoxityAbiDoc) => { + return abiDoc.type === AbiTypes.Constructor; + }); + const constructors = []; + if (!_.isUndefined(doxityConstructor)) { + const constructor = { + isConstructor: true, + name: doxityContractObj.name, + comment: doxityConstructor.details, + returnComment: doxityConstructor.return, + callPath: '', + parameters: this._convertParameters(doxityConstructor.inputs), + returnType: this._convertType(doxityContractObj.name), + }; + constructors.push(constructor); + } - const doxityMethods: DoxityAbiDoc[] = _.filter( - doxityContractObj.abiDocs, - (abiDoc: DoxityAbiDoc) => { - return this._isMethod(abiDoc); - }, - ); - const methods: SolidityMethod[] = _.map( - doxityMethods, - (doxityMethod: DoxityAbiDoc) => { - // We assume that none of our functions returns more then a single value - const outputIfExists = !_.isUndefined(doxityMethod.outputs) ? doxityMethod.outputs[0] : undefined; - const returnTypeIfExists = !_.isUndefined(outputIfExists) - ? this._convertType(outputIfExists.type) - : undefined; - // For ZRXToken, we want to convert it to zrxToken, rather then simply zRXToken - const callPath = - contractName !== 'ZRXToken' - ? `${contractName[0].toLowerCase()}${contractName.slice(1)}.` - : `${contractName.slice(0, 3).toLowerCase()}${contractName.slice(3)}.`; - const method = { - isConstructor: false, - isConstant: doxityMethod.constant, - isPayable: doxityMethod.payable, - name: doxityMethod.name, - comment: doxityMethod.details, - returnComment: doxityMethod.return, - callPath, - parameters: this._convertParameters(doxityMethod.inputs), - returnType: returnTypeIfExists, - }; - return method; - }, - ); + const doxityMethods: DoxityAbiDoc[] = _.filter( + doxityContractObj.abiDocs, + (abiDoc: DoxityAbiDoc) => { + return this._isMethod(abiDoc); + }, + ); + const methods: SolidityMethod[] = _.map( + doxityMethods, + (doxityMethod: DoxityAbiDoc) => { + // We assume that none of our functions returns more then a single value + const outputIfExists = !_.isUndefined(doxityMethod.outputs) ? doxityMethod.outputs[0] : undefined; + const returnTypeIfExists = !_.isUndefined(outputIfExists) + ? this._convertType(outputIfExists.type) + : undefined; + // For ZRXToken, we want to convert it to zrxToken, rather then simply zRXToken + const callPath = + contractName !== 'ZRXToken' + ? `${contractName[0].toLowerCase()}${contractName.slice(1)}.` + : `${contractName.slice(0, 3).toLowerCase()}${contractName.slice(3)}.`; + const method = { + isConstructor: false, + isConstant: doxityMethod.constant, + isPayable: doxityMethod.payable, + name: doxityMethod.name, + comment: doxityMethod.details, + returnComment: doxityMethod.return, + callPath, + parameters: this._convertParameters(doxityMethod.inputs), + returnType: returnTypeIfExists, + }; + return method; + }, + ); - const doxityProperties: DoxityAbiDoc[] = _.filter( - doxityContractObj.abiDocs, - (abiDoc: DoxityAbiDoc) => { - return this._isProperty(abiDoc); - }, - ); - const properties = _.map(doxityProperties, (doxityProperty: DoxityAbiDoc) => { - // We assume that none of our functions return more then a single return value - let typeName = doxityProperty.outputs[0].type; - if (!_.isEmpty(doxityProperty.inputs)) { - // Properties never have more then a single input - typeName = `(${doxityProperty.inputs[0].type} => ${typeName})`; - } - const property = { - name: doxityProperty.name, - type: this._convertType(typeName), - comment: doxityProperty.details, - }; - return property; - }); + const doxityProperties: DoxityAbiDoc[] = _.filter( + doxityContractObj.abiDocs, + (abiDoc: DoxityAbiDoc) => { + return this._isProperty(abiDoc); + }, + ); + const properties = _.map(doxityProperties, (doxityProperty: DoxityAbiDoc) => { + // We assume that none of our functions return more then a single return value + let typeName = doxityProperty.outputs[0].type; + if (!_.isEmpty(doxityProperty.inputs)) { + // Properties never have more then a single input + typeName = `(${doxityProperty.inputs[0].type} => ${typeName})`; + } + const property = { + name: doxityProperty.name, + type: this._convertType(typeName), + comment: doxityProperty.details, + }; + return property; + }); - const doxityEvents = _.filter( - doxityContractObj.abiDocs, - (abiDoc: DoxityAbiDoc) => abiDoc.type === AbiTypes.Event, - ); - const events = _.map(doxityEvents, doxityEvent => { - const event = { - name: doxityEvent.name, - eventArgs: this._convertEventArgs(doxityEvent.inputs), - }; - return event; - }); + const doxityEvents = _.filter( + doxityContractObj.abiDocs, + (abiDoc: DoxityAbiDoc) => abiDoc.type === AbiTypes.Event, + ); + const events = _.map(doxityEvents, doxityEvent => { + const event = { + name: doxityEvent.name, + eventArgs: this._convertEventArgs(doxityEvent.inputs), + }; + return event; + }); - const docSection: DocSection = { - comment: doxityContractObj.title, - constructors, - methods, - properties, - types: [], - events, - }; - docAgnosticFormat[contractName] = docSection; - }); - return docAgnosticFormat; - }, - _convertParameters(inputs: DoxityInput[]): Parameter[] { - const parameters = _.map(inputs, input => { - const parameter = { - name: input.name, - comment: input.description, - isOptional: false, - type: this._convertType(input.type), - }; - return parameter; - }); - return parameters; - }, - _convertType(typeName: string): Type { - const type = { - name: typeName, - typeDocType: TypeDocTypes.Intrinsic, - }; - return type; - }, - _isMethod(abiDoc: DoxityAbiDoc) { - if (abiDoc.type !== AbiTypes.Function) { - return false; - } - const hasInputs = !_.isEmpty(abiDoc.inputs); - const hasNamedOutputIfExists = !hasInputs || !_.isEmpty(abiDoc.inputs[0].name); - const isNameAllCaps = abiDoc.name === abiDoc.name.toUpperCase(); - const isMethod = hasNamedOutputIfExists && !isNameAllCaps; - return isMethod; - }, - _isProperty(abiDoc: DoxityAbiDoc) { - if (abiDoc.type !== AbiTypes.Function) { - return false; - } - const hasInputs = !_.isEmpty(abiDoc.inputs); - const hasNamedOutputIfExists = !hasInputs || !_.isEmpty(abiDoc.inputs[0].name); - const isNameAllCaps = abiDoc.name === abiDoc.name.toUpperCase(); - const isProperty = !hasNamedOutputIfExists || isNameAllCaps; - return isProperty; - }, - _convertEventArgs(inputs: DoxityInput[]): EventArg[] { - const eventArgs = _.map(inputs, input => { - const eventArg = { - isIndexed: input.indexed, - name: input.name, - type: this._convertType(input.type), - }; - return eventArg; - }); - return eventArgs; - }, + const docSection: DocSection = { + comment: doxityContractObj.title, + constructors, + methods, + properties, + types: [], + events, + }; + docAgnosticFormat[contractName] = docSection; + }); + return docAgnosticFormat; + }, + _convertParameters(inputs: DoxityInput[]): Parameter[] { + const parameters = _.map(inputs, input => { + const parameter = { + name: input.name, + comment: input.description, + isOptional: false, + type: this._convertType(input.type), + }; + return parameter; + }); + return parameters; + }, + _convertType(typeName: string): Type { + const type = { + name: typeName, + typeDocType: TypeDocTypes.Intrinsic, + }; + return type; + }, + _isMethod(abiDoc: DoxityAbiDoc) { + if (abiDoc.type !== AbiTypes.Function) { + return false; + } + const hasInputs = !_.isEmpty(abiDoc.inputs); + const hasNamedOutputIfExists = !hasInputs || !_.isEmpty(abiDoc.inputs[0].name); + const isNameAllCaps = abiDoc.name === abiDoc.name.toUpperCase(); + const isMethod = hasNamedOutputIfExists && !isNameAllCaps; + return isMethod; + }, + _isProperty(abiDoc: DoxityAbiDoc) { + if (abiDoc.type !== AbiTypes.Function) { + return false; + } + const hasInputs = !_.isEmpty(abiDoc.inputs); + const hasNamedOutputIfExists = !hasInputs || !_.isEmpty(abiDoc.inputs[0].name); + const isNameAllCaps = abiDoc.name === abiDoc.name.toUpperCase(); + const isProperty = !hasNamedOutputIfExists || isNameAllCaps; + return isProperty; + }, + _convertEventArgs(inputs: DoxityInput[]): EventArg[] { + const eventArgs = _.map(inputs, input => { + const eventArg = { + isIndexed: input.indexed, + name: input.name, + type: this._convertType(input.type), + }; + return eventArg; + }); + return eventArgs; + }, }; diff --git a/packages/website/ts/utils/error_reporter.ts b/packages/website/ts/utils/error_reporter.ts index 08d99e405..0bd247c5b 100644 --- a/packages/website/ts/utils/error_reporter.ts +++ b/packages/website/ts/utils/error_reporter.ts @@ -6,47 +6,47 @@ import { utils } from 'ts/utils/utils'; // Suggested way to include Rollbar with Webpack // https://github.com/rollbar/rollbar.js/tree/master/examples/webpack const rollbarConfig = { - accessToken: constants.ROLLBAR_ACCESS_TOKEN, - captureUncaught: true, - captureUnhandledRejections: true, - itemsPerMinute: 10, - maxItems: 500, - payload: { - environment: configs.ENVIRONMENT, - }, - uncaughtErrorLevel: 'error', - hostWhiteList: [configs.DOMAIN_PRODUCTION, configs.DOMAIN_STAGING], - ignoredMessages: [ - // Errors from the third-party scripts - 'Script error', - // Network errors or ad-blockers - 'TypeError: Failed to fetch', - 'Exchange has not been deployed to detected network (network/artifact mismatch)', - // Source: https://groups.google.com/a/chromium.org/forum/#!topic/chromium-discuss/7VU0_VvC7mE - "undefined is not an object (evaluating '__gCrWeb.autofill.extractForms')", - // Source: http://stackoverflow.com/questions/43399818/securityerror-from-facebook-and-cross-domain-messaging - 'SecurityError (DOM Exception 18)', - ], + accessToken: constants.ROLLBAR_ACCESS_TOKEN, + captureUncaught: true, + captureUnhandledRejections: true, + itemsPerMinute: 10, + maxItems: 500, + payload: { + environment: configs.ENVIRONMENT, + }, + uncaughtErrorLevel: 'error', + hostWhiteList: [configs.DOMAIN_PRODUCTION, configs.DOMAIN_STAGING], + ignoredMessages: [ + // Errors from the third-party scripts + 'Script error', + // Network errors or ad-blockers + 'TypeError: Failed to fetch', + 'Exchange has not been deployed to detected network (network/artifact mismatch)', + // Source: https://groups.google.com/a/chromium.org/forum/#!topic/chromium-discuss/7VU0_VvC7mE + "undefined is not an object (evaluating '__gCrWeb.autofill.extractForms')", + // Source: http://stackoverflow.com/questions/43399818/securityerror-from-facebook-and-cross-domain-messaging + 'SecurityError (DOM Exception 18)', + ], }; import Rollbar = require('../../public/js/rollbar.umd.nojson.min.js'); const rollbar = Rollbar.init(rollbarConfig); export const errorReporter = { - async reportAsync(err: Error): Promise { - if (configs.ENVIRONMENT === Environments.DEVELOPMENT) { - return; // Let's not log development errors to rollbar - } + async reportAsync(err: Error): Promise { + if (configs.ENVIRONMENT === Environments.DEVELOPMENT) { + return; // Let's not log development errors to rollbar + } - return new Promise((resolve, reject) => { - rollbar.error(err, (rollbarErr: Error) => { - if (rollbarErr) { - utils.consoleLog(`Error reporting to rollbar, ignoring: ${rollbarErr}`); - // We never want to reject and cause the app to throw because of rollbar - resolve(); - } else { - resolve(); - } - }); - }); - }, + return new Promise((resolve, reject) => { + rollbar.error(err, (rollbarErr: Error) => { + if (rollbarErr) { + utils.consoleLog(`Error reporting to rollbar, ignoring: ${rollbarErr}`); + // We never want to reject and cause the app to throw because of rollbar + resolve(); + } else { + resolve(); + } + }); + }); + }, }; diff --git a/packages/website/ts/utils/mui_theme.ts b/packages/website/ts/utils/mui_theme.ts index 565d1ae4f..d73e80606 100644 --- a/packages/website/ts/utils/mui_theme.ts +++ b/packages/website/ts/utils/mui_theme.ts @@ -2,34 +2,34 @@ import { getMuiTheme } from 'material-ui/styles'; import { colors } from 'ts/utils/colors'; export const muiTheme = getMuiTheme({ - appBar: { - height: 45, - color: colors.white, - textColor: colors.black, - }, - palette: { - pickerHeaderColor: colors.lightBlue, - primary1Color: colors.lightBlue, - primary2Color: colors.lightBlue, - textColor: colors.grey700, - }, - datePicker: { - color: colors.grey700, - textColor: colors.white, - calendarTextColor: colors.white, - selectColor: colors.darkestGrey, - selectTextColor: colors.white, - }, - timePicker: { - color: colors.grey700, - textColor: colors.white, - accentColor: colors.white, - headerColor: colors.darkestGrey, - selectColor: colors.darkestGrey, - selectTextColor: colors.darkestGrey, - }, - toggle: { - thumbOnColor: colors.limeGreen, - trackOnColor: colors.lightGreen, - }, + appBar: { + height: 45, + color: colors.white, + textColor: colors.black, + }, + palette: { + pickerHeaderColor: colors.lightBlue, + primary1Color: colors.lightBlue, + primary2Color: colors.lightBlue, + textColor: colors.grey700, + }, + datePicker: { + color: colors.grey700, + textColor: colors.white, + calendarTextColor: colors.white, + selectColor: colors.darkestGrey, + selectTextColor: colors.white, + }, + timePicker: { + color: colors.grey700, + textColor: colors.white, + accentColor: colors.white, + headerColor: colors.darkestGrey, + selectColor: colors.darkestGrey, + selectTextColor: colors.darkestGrey, + }, + toggle: { + thumbOnColor: colors.limeGreen, + trackOnColor: colors.lightGreen, + }, }); diff --git a/packages/website/ts/utils/typedoc_utils.ts b/packages/website/ts/utils/typedoc_utils.ts index b0c152891..11ec8da58 100644 --- a/packages/website/ts/utils/typedoc_utils.ts +++ b/packages/website/ts/utils/typedoc_utils.ts @@ -1,365 +1,365 @@ import * as _ from 'lodash'; import { DocsInfo } from 'ts/pages/documentation/docs_info'; import { - CustomType, - CustomTypeChild, - DocAgnosticFormat, - DocSection, - IndexSignature, - KindString, - Parameter, - Property, - SectionsMap, - Type, - TypeDocNode, - TypeDocType, - TypeParameter, - TypescriptMethod, + CustomType, + CustomTypeChild, + DocAgnosticFormat, + DocSection, + IndexSignature, + KindString, + Parameter, + Property, + SectionsMap, + Type, + TypeDocNode, + TypeDocType, + TypeParameter, + TypescriptMethod, } from 'ts/types'; import { utils } from 'ts/utils/utils'; export const typeDocUtils = { - isType(entity: TypeDocNode): boolean { - return ( - entity.kindString === KindString.Interface || - entity.kindString === KindString.Function || - entity.kindString === KindString.TypeAlias || - entity.kindString === KindString.Variable || - entity.kindString === KindString.Enumeration - ); - }, - isMethod(entity: TypeDocNode): boolean { - return entity.kindString === KindString.Method; - }, - isConstructor(entity: TypeDocNode): boolean { - return entity.kindString === KindString.Constructor; - }, - isProperty(entity: TypeDocNode): boolean { - return entity.kindString === KindString.Property; - }, - isPrivateOrProtectedProperty(propertyName: string): boolean { - return _.startsWith(propertyName, '_'); - }, - getModuleDefinitionBySectionNameIfExists( - versionDocObj: TypeDocNode, - modulePaths: string[], - ): TypeDocNode | undefined { - const modules = versionDocObj.children; - for (const mod of modules) { - if (_.includes(modulePaths, mod.name)) { - const moduleWithName = mod; - return moduleWithName; - } - } - return undefined; - }, - convertToDocAgnosticFormat(typeDocJson: TypeDocNode, docsInfo: DocsInfo): DocAgnosticFormat { - const subMenus = _.values(docsInfo.getMenu()); - const orderedSectionNames = _.flatten(subMenus); - const docAgnosticFormat: DocAgnosticFormat = {}; - _.each(orderedSectionNames, sectionName => { - const modulePathsIfExists = docsInfo.getModulePathsIfExists(sectionName); - if (_.isUndefined(modulePathsIfExists)) { - return; // no-op - } - const packageDefinitionIfExists = typeDocUtils.getModuleDefinitionBySectionNameIfExists( - typeDocJson, - modulePathsIfExists, - ); - if (_.isUndefined(packageDefinitionIfExists)) { - return; // no-op - } + isType(entity: TypeDocNode): boolean { + return ( + entity.kindString === KindString.Interface || + entity.kindString === KindString.Function || + entity.kindString === KindString.TypeAlias || + entity.kindString === KindString.Variable || + entity.kindString === KindString.Enumeration + ); + }, + isMethod(entity: TypeDocNode): boolean { + return entity.kindString === KindString.Method; + }, + isConstructor(entity: TypeDocNode): boolean { + return entity.kindString === KindString.Constructor; + }, + isProperty(entity: TypeDocNode): boolean { + return entity.kindString === KindString.Property; + }, + isPrivateOrProtectedProperty(propertyName: string): boolean { + return _.startsWith(propertyName, '_'); + }, + getModuleDefinitionBySectionNameIfExists( + versionDocObj: TypeDocNode, + modulePaths: string[], + ): TypeDocNode | undefined { + const modules = versionDocObj.children; + for (const mod of modules) { + if (_.includes(modulePaths, mod.name)) { + const moduleWithName = mod; + return moduleWithName; + } + } + return undefined; + }, + convertToDocAgnosticFormat(typeDocJson: TypeDocNode, docsInfo: DocsInfo): DocAgnosticFormat { + const subMenus = _.values(docsInfo.getMenu()); + const orderedSectionNames = _.flatten(subMenus); + const docAgnosticFormat: DocAgnosticFormat = {}; + _.each(orderedSectionNames, sectionName => { + const modulePathsIfExists = docsInfo.getModulePathsIfExists(sectionName); + if (_.isUndefined(modulePathsIfExists)) { + return; // no-op + } + const packageDefinitionIfExists = typeDocUtils.getModuleDefinitionBySectionNameIfExists( + typeDocJson, + modulePathsIfExists, + ); + if (_.isUndefined(packageDefinitionIfExists)) { + return; // no-op + } - // Since the `types.ts` file is the only file that does not export a module/class but - // instead has each type export itself, we do not need to go down two levels of nesting - // for it. - let entities; - let packageComment = ''; - if (sectionName === docsInfo.sections.types) { - entities = packageDefinitionIfExists.children; - } else { - entities = packageDefinitionIfExists.children[0].children; - const commentObj = packageDefinitionIfExists.children[0].comment; - packageComment = !_.isUndefined(commentObj) ? commentObj.shortText : packageComment; - } + // Since the `types.ts` file is the only file that does not export a module/class but + // instead has each type export itself, we do not need to go down two levels of nesting + // for it. + let entities; + let packageComment = ''; + if (sectionName === docsInfo.sections.types) { + entities = packageDefinitionIfExists.children; + } else { + entities = packageDefinitionIfExists.children[0].children; + const commentObj = packageDefinitionIfExists.children[0].comment; + packageComment = !_.isUndefined(commentObj) ? commentObj.shortText : packageComment; + } - const docSection = typeDocUtils._convertEntitiesToDocSection(entities, docsInfo, sectionName); - docSection.comment = packageComment; - docAgnosticFormat[sectionName] = docSection; - }); - return docAgnosticFormat; - }, - _convertEntitiesToDocSection(entities: TypeDocNode[], docsInfo: DocsInfo, sectionName: string) { - const docSection: DocSection = { - comment: '', - constructors: [], - methods: [], - properties: [], - types: [], - }; + const docSection = typeDocUtils._convertEntitiesToDocSection(entities, docsInfo, sectionName); + docSection.comment = packageComment; + docAgnosticFormat[sectionName] = docSection; + }); + return docAgnosticFormat; + }, + _convertEntitiesToDocSection(entities: TypeDocNode[], docsInfo: DocsInfo, sectionName: string) { + const docSection: DocSection = { + comment: '', + constructors: [], + methods: [], + properties: [], + types: [], + }; - let isConstructor; - _.each(entities, entity => { - switch (entity.kindString) { - case KindString.Constructor: - isConstructor = true; - const constructor = typeDocUtils._convertMethod( - entity, - isConstructor, - docsInfo.sections, - sectionName, - docsInfo.subPackageName, - ); - docSection.constructors.push(constructor); - break; + let isConstructor; + _.each(entities, entity => { + switch (entity.kindString) { + case KindString.Constructor: + isConstructor = true; + const constructor = typeDocUtils._convertMethod( + entity, + isConstructor, + docsInfo.sections, + sectionName, + docsInfo.subPackageName, + ); + docSection.constructors.push(constructor); + break; - case KindString.Method: - if (entity.flags.isPublic) { - isConstructor = false; - const method = typeDocUtils._convertMethod( - entity, - isConstructor, - docsInfo.sections, - sectionName, - docsInfo.subPackageName, - ); - docSection.methods.push(method); - } - break; + case KindString.Method: + if (entity.flags.isPublic) { + isConstructor = false; + const method = typeDocUtils._convertMethod( + entity, + isConstructor, + docsInfo.sections, + sectionName, + docsInfo.subPackageName, + ); + docSection.methods.push(method); + } + break; - case KindString.Property: - if (!typeDocUtils.isPrivateOrProtectedProperty(entity.name)) { - const property = typeDocUtils._convertProperty( - entity, - docsInfo.sections, - sectionName, - docsInfo.subPackageName, - ); - docSection.properties.push(property); - } - break; + case KindString.Property: + if (!typeDocUtils.isPrivateOrProtectedProperty(entity.name)) { + const property = typeDocUtils._convertProperty( + entity, + docsInfo.sections, + sectionName, + docsInfo.subPackageName, + ); + docSection.properties.push(property); + } + break; - case KindString.Interface: - case KindString.Function: - case KindString.Variable: - case KindString.Enumeration: - case KindString.TypeAlias: - if (docsInfo.isPublicType(entity.name)) { - const customType = typeDocUtils._convertCustomType( - entity, - docsInfo.sections, - sectionName, - docsInfo.subPackageName, - ); - docSection.types.push(customType); - } - break; + case KindString.Interface: + case KindString.Function: + case KindString.Variable: + case KindString.Enumeration: + case KindString.TypeAlias: + if (docsInfo.isPublicType(entity.name)) { + const customType = typeDocUtils._convertCustomType( + entity, + docsInfo.sections, + sectionName, + docsInfo.subPackageName, + ); + docSection.types.push(customType); + } + break; - default: - throw utils.spawnSwitchErr('kindString', entity.kindString); - } - }); - return docSection; - }, - _convertCustomType( - entity: TypeDocNode, - sections: SectionsMap, - sectionName: string, - subPackageName: string, - ): CustomType { - const typeIfExists = !_.isUndefined(entity.type) - ? typeDocUtils._convertType(entity.type, sections, sectionName, subPackageName) - : undefined; - const isConstructor = false; - const methodIfExists = !_.isUndefined(entity.declaration) - ? typeDocUtils._convertMethod(entity.declaration, isConstructor, sections, sectionName, subPackageName) - : undefined; - const indexSignatureIfExists = !_.isUndefined(entity.indexSignature) - ? typeDocUtils._convertIndexSignature(entity.indexSignature[0], sections, sectionName, subPackageName) - : undefined; - const commentIfExists = - !_.isUndefined(entity.comment) && !_.isUndefined(entity.comment.shortText) - ? entity.comment.shortText - : undefined; + default: + throw utils.spawnSwitchErr('kindString', entity.kindString); + } + }); + return docSection; + }, + _convertCustomType( + entity: TypeDocNode, + sections: SectionsMap, + sectionName: string, + subPackageName: string, + ): CustomType { + const typeIfExists = !_.isUndefined(entity.type) + ? typeDocUtils._convertType(entity.type, sections, sectionName, subPackageName) + : undefined; + const isConstructor = false; + const methodIfExists = !_.isUndefined(entity.declaration) + ? typeDocUtils._convertMethod(entity.declaration, isConstructor, sections, sectionName, subPackageName) + : undefined; + const indexSignatureIfExists = !_.isUndefined(entity.indexSignature) + ? typeDocUtils._convertIndexSignature(entity.indexSignature[0], sections, sectionName, subPackageName) + : undefined; + const commentIfExists = + !_.isUndefined(entity.comment) && !_.isUndefined(entity.comment.shortText) + ? entity.comment.shortText + : undefined; - const childrenIfExist = !_.isUndefined(entity.children) - ? _.map(entity.children, (child: TypeDocNode) => { - const childTypeIfExists = !_.isUndefined(child.type) - ? typeDocUtils._convertType(child.type, sections, sectionName, subPackageName) - : undefined; - const c: CustomTypeChild = { - name: child.name, - type: childTypeIfExists, - defaultValue: child.defaultValue, - }; - return c; - }) - : undefined; + const childrenIfExist = !_.isUndefined(entity.children) + ? _.map(entity.children, (child: TypeDocNode) => { + const childTypeIfExists = !_.isUndefined(child.type) + ? typeDocUtils._convertType(child.type, sections, sectionName, subPackageName) + : undefined; + const c: CustomTypeChild = { + name: child.name, + type: childTypeIfExists, + defaultValue: child.defaultValue, + }; + return c; + }) + : undefined; - const customType = { - name: entity.name, - kindString: entity.kindString, - type: typeIfExists, - method: methodIfExists, - indexSignature: indexSignatureIfExists, - defaultValue: entity.defaultValue, - comment: commentIfExists, - children: childrenIfExist, - }; - return customType; - }, - _convertIndexSignature( - entity: TypeDocNode, - sections: SectionsMap, - sectionName: string, - subPackageName: string, - ): IndexSignature { - const key = entity.parameters[0]; - const indexSignature = { - keyName: key.name, - keyType: typeDocUtils._convertType(key.type, sections, sectionName, subPackageName), - valueName: entity.type.name, - }; - return indexSignature; - }, - _convertProperty( - entity: TypeDocNode, - sections: SectionsMap, - sectionName: string, - subPackageName: string, - ): Property { - const source = entity.sources[0]; - const commentIfExists = !_.isUndefined(entity.comment) ? entity.comment.shortText : undefined; - const property = { - name: entity.name, - type: typeDocUtils._convertType(entity.type, sections, sectionName, subPackageName), - source: { - fileName: source.fileName, - line: source.line, - }, - comment: commentIfExists, - }; - return property; - }, - _convertMethod( - entity: TypeDocNode, - isConstructor: boolean, - sections: SectionsMap, - sectionName: string, - subPackageName: string, - ): TypescriptMethod { - const signature = entity.signatures[0]; - const source = entity.sources[0]; - const hasComment = !_.isUndefined(signature.comment); - const isStatic = _.isUndefined(entity.flags.isStatic) ? false : entity.flags.isStatic; + const customType = { + name: entity.name, + kindString: entity.kindString, + type: typeIfExists, + method: methodIfExists, + indexSignature: indexSignatureIfExists, + defaultValue: entity.defaultValue, + comment: commentIfExists, + children: childrenIfExist, + }; + return customType; + }, + _convertIndexSignature( + entity: TypeDocNode, + sections: SectionsMap, + sectionName: string, + subPackageName: string, + ): IndexSignature { + const key = entity.parameters[0]; + const indexSignature = { + keyName: key.name, + keyType: typeDocUtils._convertType(key.type, sections, sectionName, subPackageName), + valueName: entity.type.name, + }; + return indexSignature; + }, + _convertProperty( + entity: TypeDocNode, + sections: SectionsMap, + sectionName: string, + subPackageName: string, + ): Property { + const source = entity.sources[0]; + const commentIfExists = !_.isUndefined(entity.comment) ? entity.comment.shortText : undefined; + const property = { + name: entity.name, + type: typeDocUtils._convertType(entity.type, sections, sectionName, subPackageName), + source: { + fileName: source.fileName, + line: source.line, + }, + comment: commentIfExists, + }; + return property; + }, + _convertMethod( + entity: TypeDocNode, + isConstructor: boolean, + sections: SectionsMap, + sectionName: string, + subPackageName: string, + ): TypescriptMethod { + const signature = entity.signatures[0]; + const source = entity.sources[0]; + const hasComment = !_.isUndefined(signature.comment); + const isStatic = _.isUndefined(entity.flags.isStatic) ? false : entity.flags.isStatic; - // HACK: we use the fact that the sectionName is the same as the property name at the top-level - // of the public interface. In the future, we shouldn't use this hack but rather get it from the JSON. - let callPath; - if (isConstructor || entity.name === '__type') { - callPath = ''; - } else if (subPackageName === '0x.js') { - const topLevelInterface = isStatic ? 'ZeroEx.' : 'zeroEx.'; - callPath = - !_.isUndefined(sections.zeroEx) && sectionName !== sections.zeroEx - ? `${topLevelInterface}${sectionName}.` - : topLevelInterface; - } else { - callPath = `${sectionName}.`; - } + // HACK: we use the fact that the sectionName is the same as the property name at the top-level + // of the public interface. In the future, we shouldn't use this hack but rather get it from the JSON. + let callPath; + if (isConstructor || entity.name === '__type') { + callPath = ''; + } else if (subPackageName === '0x.js') { + const topLevelInterface = isStatic ? 'ZeroEx.' : 'zeroEx.'; + callPath = + !_.isUndefined(sections.zeroEx) && sectionName !== sections.zeroEx + ? `${topLevelInterface}${sectionName}.` + : topLevelInterface; + } else { + callPath = `${sectionName}.`; + } - const parameters = _.map(signature.parameters, param => { - return typeDocUtils._convertParameter(param, sections, sectionName, subPackageName); - }); - const returnType = typeDocUtils._convertType(signature.type, sections, sectionName, subPackageName); - const typeParameter = _.isUndefined(signature.typeParameter) - ? undefined - : typeDocUtils._convertTypeParameter(signature.typeParameter[0], sections, sectionName, subPackageName); + const parameters = _.map(signature.parameters, param => { + return typeDocUtils._convertParameter(param, sections, sectionName, subPackageName); + }); + const returnType = typeDocUtils._convertType(signature.type, sections, sectionName, subPackageName); + const typeParameter = _.isUndefined(signature.typeParameter) + ? undefined + : typeDocUtils._convertTypeParameter(signature.typeParameter[0], sections, sectionName, subPackageName); - const method = { - isConstructor, - isStatic, - name: signature.name, - comment: hasComment ? signature.comment.shortText : undefined, - returnComment: hasComment && signature.comment.returns ? signature.comment.returns : undefined, - source: { - fileName: source.fileName, - line: source.line, - }, - callPath, - parameters, - returnType, - typeParameter, - }; - return method; - }, - _convertTypeParameter( - entity: TypeDocNode, - sections: SectionsMap, - sectionName: string, - subPackageName: string, - ): TypeParameter { - const type = typeDocUtils._convertType(entity.type, sections, sectionName, subPackageName); - const parameter = { - name: entity.name, - type, - }; - return parameter; - }, - _convertParameter( - entity: TypeDocNode, - sections: SectionsMap, - sectionName: string, - subPackageName: string, - ): Parameter { - let comment = ''; - if (entity.comment && entity.comment.shortText) { - comment = entity.comment.shortText; - } else if (entity.comment && entity.comment.text) { - comment = entity.comment.text; - } + const method = { + isConstructor, + isStatic, + name: signature.name, + comment: hasComment ? signature.comment.shortText : undefined, + returnComment: hasComment && signature.comment.returns ? signature.comment.returns : undefined, + source: { + fileName: source.fileName, + line: source.line, + }, + callPath, + parameters, + returnType, + typeParameter, + }; + return method; + }, + _convertTypeParameter( + entity: TypeDocNode, + sections: SectionsMap, + sectionName: string, + subPackageName: string, + ): TypeParameter { + const type = typeDocUtils._convertType(entity.type, sections, sectionName, subPackageName); + const parameter = { + name: entity.name, + type, + }; + return parameter; + }, + _convertParameter( + entity: TypeDocNode, + sections: SectionsMap, + sectionName: string, + subPackageName: string, + ): Parameter { + let comment = ''; + if (entity.comment && entity.comment.shortText) { + comment = entity.comment.shortText; + } else if (entity.comment && entity.comment.text) { + comment = entity.comment.text; + } - const isOptional = !_.isUndefined(entity.flags.isOptional) ? entity.flags.isOptional : false; + const isOptional = !_.isUndefined(entity.flags.isOptional) ? entity.flags.isOptional : false; - const type = typeDocUtils._convertType(entity.type, sections, sectionName, subPackageName); + const type = typeDocUtils._convertType(entity.type, sections, sectionName, subPackageName); - const parameter = { - name: entity.name, - comment, - isOptional, - type, - }; - return parameter; - }, - _convertType(entity: TypeDocType, sections: SectionsMap, sectionName: string, subPackageName: string): Type { - const typeArguments = _.map(entity.typeArguments, typeArgument => { - return typeDocUtils._convertType(typeArgument, sections, sectionName, subPackageName); - }); - const types = _.map(entity.types, t => { - return typeDocUtils._convertType(t, sections, sectionName, subPackageName); - }); + const parameter = { + name: entity.name, + comment, + isOptional, + type, + }; + return parameter; + }, + _convertType(entity: TypeDocType, sections: SectionsMap, sectionName: string, subPackageName: string): Type { + const typeArguments = _.map(entity.typeArguments, typeArgument => { + return typeDocUtils._convertType(typeArgument, sections, sectionName, subPackageName); + }); + const types = _.map(entity.types, t => { + return typeDocUtils._convertType(t, sections, sectionName, subPackageName); + }); - const isConstructor = false; - const methodIfExists = !_.isUndefined(entity.declaration) - ? typeDocUtils._convertMethod(entity.declaration, isConstructor, sections, sectionName, subPackageName) - : undefined; + const isConstructor = false; + const methodIfExists = !_.isUndefined(entity.declaration) + ? typeDocUtils._convertMethod(entity.declaration, isConstructor, sections, sectionName, subPackageName) + : undefined; - const elementTypeIfExists = !_.isUndefined(entity.elementType) - ? { - name: entity.elementType.name, - typeDocType: entity.elementType.type, - } - : undefined; + const elementTypeIfExists = !_.isUndefined(entity.elementType) + ? { + name: entity.elementType.name, + typeDocType: entity.elementType.type, + } + : undefined; - const type = { - name: entity.name, - value: entity.value, - typeDocType: entity.type, - typeArguments, - elementType: elementTypeIfExists, - types, - method: methodIfExists, - }; - return type; - }, + const type = { + name: entity.name, + value: entity.value, + typeDocType: entity.type, + typeArguments, + elementType: elementTypeIfExists, + types, + method: methodIfExists, + }; + return type; + }, }; diff --git a/packages/website/ts/utils/utils.ts b/packages/website/ts/utils/utils.ts index ea5b689ae..13a6d6ae2 100644 --- a/packages/website/ts/utils/utils.ts +++ b/packages/website/ts/utils/utils.ts @@ -5,15 +5,15 @@ import isMobile = require('is-mobile'); import * as _ from 'lodash'; import * as moment from 'moment'; import { - EtherscanLinkSuffixes, - Networks, - Order, - ScreenWidths, - Side, - SideToAssetToken, - SignatureData, - Token, - TokenByAddress, + EtherscanLinkSuffixes, + Networks, + Order, + ScreenWidths, + Side, + SideToAssetToken, + SignatureData, + Token, + TokenByAddress, } from 'ts/types'; import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; @@ -23,257 +23,257 @@ const LG_MIN_EM = 64; const MD_MIN_EM = 52; export const utils = { - assert(condition: boolean, message: string) { - if (!condition) { - throw new Error(message); - } - }, - spawnSwitchErr(name: string, value: any) { - return new Error(`Unexpected switch value: ${value} encountered for ${name}`); - }, - isNumeric(n: string) { - return !isNaN(parseFloat(n)) && isFinite(Number(n)); - }, - // This default unix timestamp is used for orders where the user does not specify an expiry date. - // It is a fixed constant so that both the redux store's INITIAL_STATE and components can check for - // whether a user has set an expiry date or not. It is set unrealistically high so as not to collide - // with actual values a user would select. - initialOrderExpiryUnixTimestampSec(): BigNumber { - const m = moment('2050-01-01'); - return new BigNumber(m.unix()); - }, - convertToUnixTimestampSeconds(date: moment.Moment, time?: moment.Moment): BigNumber { - const finalMoment = date; - if (!_.isUndefined(time)) { - finalMoment.hours(time.hours()); - finalMoment.minutes(time.minutes()); - } - return new BigNumber(finalMoment.unix()); - }, - convertToMomentFromUnixTimestamp(unixTimestampSec: BigNumber): moment.Moment { - return moment.unix(unixTimestampSec.toNumber()); - }, - convertToReadableDateTimeFromUnixTimestamp(unixTimestampSec: BigNumber): string { - const m = this.convertToMomentFromUnixTimestamp(unixTimestampSec); - const formattedDate: string = m.format('h:MMa MMMM D YYYY'); - return formattedDate; - }, - generateOrder( - networkId: number, - exchangeContract: string, - sideToAssetToken: SideToAssetToken, - orderExpiryTimestamp: BigNumber, - orderTakerAddress: string, - orderMakerAddress: string, - makerFee: BigNumber, - takerFee: BigNumber, - feeRecipient: string, - signatureData: SignatureData, - tokenByAddress: TokenByAddress, - orderSalt: BigNumber, - ): Order { - const makerToken = tokenByAddress[sideToAssetToken[Side.Deposit].address]; - const takerToken = tokenByAddress[sideToAssetToken[Side.Receive].address]; - const order = { - maker: { - address: orderMakerAddress, - token: { - name: makerToken.name, - symbol: makerToken.symbol, - decimals: makerToken.decimals, - address: makerToken.address, - }, - amount: sideToAssetToken[Side.Deposit].amount.toString(), - feeAmount: makerFee.toString(), - }, - taker: { - address: orderTakerAddress, - token: { - name: takerToken.name, - symbol: takerToken.symbol, - decimals: takerToken.decimals, - address: takerToken.address, - }, - amount: sideToAssetToken[Side.Receive].amount.toString(), - feeAmount: takerFee.toString(), - }, - expiration: orderExpiryTimestamp.toString(), - feeRecipient, - salt: orderSalt.toString(), - signature: signatureData, - exchangeContract, - networkId, - }; - return order; - }, - consoleLog(message: string) { - /* tslint:disable */ - console.log(message); - /* tslint:enable */ - }, - async sleepAsync(ms: number) { - return new Promise(resolve => setTimeout(resolve, ms)); - }, - deepEqual(actual: any, expected: any, opts?: { strict: boolean }) { - return deepEqual(actual, expected, opts); - }, - getColSize(items: number) { - const bassCssGridSize = 12; // Source: http://basscss.com/#basscss-grid - const colSize = bassCssGridSize / items; - if (!_.isInteger(colSize)) { - throw new Error(`Number of cols must be divisible by ${bassCssGridSize}`); - } - return colSize; - }, - getScreenWidth() { - const documentEl = document.documentElement; - const body = document.getElementsByTagName('body')[0]; - const widthInPx = window.innerWidth || documentEl.clientWidth || body.clientWidth; - const bodyStyles: any = window.getComputedStyle(document.querySelector('body')); - const widthInEm = widthInPx / parseFloat(bodyStyles['font-size']); + assert(condition: boolean, message: string) { + if (!condition) { + throw new Error(message); + } + }, + spawnSwitchErr(name: string, value: any) { + return new Error(`Unexpected switch value: ${value} encountered for ${name}`); + }, + isNumeric(n: string) { + return !isNaN(parseFloat(n)) && isFinite(Number(n)); + }, + // This default unix timestamp is used for orders where the user does not specify an expiry date. + // It is a fixed constant so that both the redux store's INITIAL_STATE and components can check for + // whether a user has set an expiry date or not. It is set unrealistically high so as not to collide + // with actual values a user would select. + initialOrderExpiryUnixTimestampSec(): BigNumber { + const m = moment('2050-01-01'); + return new BigNumber(m.unix()); + }, + convertToUnixTimestampSeconds(date: moment.Moment, time?: moment.Moment): BigNumber { + const finalMoment = date; + if (!_.isUndefined(time)) { + finalMoment.hours(time.hours()); + finalMoment.minutes(time.minutes()); + } + return new BigNumber(finalMoment.unix()); + }, + convertToMomentFromUnixTimestamp(unixTimestampSec: BigNumber): moment.Moment { + return moment.unix(unixTimestampSec.toNumber()); + }, + convertToReadableDateTimeFromUnixTimestamp(unixTimestampSec: BigNumber): string { + const m = this.convertToMomentFromUnixTimestamp(unixTimestampSec); + const formattedDate: string = m.format('h:MMa MMMM D YYYY'); + return formattedDate; + }, + generateOrder( + networkId: number, + exchangeContract: string, + sideToAssetToken: SideToAssetToken, + orderExpiryTimestamp: BigNumber, + orderTakerAddress: string, + orderMakerAddress: string, + makerFee: BigNumber, + takerFee: BigNumber, + feeRecipient: string, + signatureData: SignatureData, + tokenByAddress: TokenByAddress, + orderSalt: BigNumber, + ): Order { + const makerToken = tokenByAddress[sideToAssetToken[Side.Deposit].address]; + const takerToken = tokenByAddress[sideToAssetToken[Side.Receive].address]; + const order = { + maker: { + address: orderMakerAddress, + token: { + name: makerToken.name, + symbol: makerToken.symbol, + decimals: makerToken.decimals, + address: makerToken.address, + }, + amount: sideToAssetToken[Side.Deposit].amount.toString(), + feeAmount: makerFee.toString(), + }, + taker: { + address: orderTakerAddress, + token: { + name: takerToken.name, + symbol: takerToken.symbol, + decimals: takerToken.decimals, + address: takerToken.address, + }, + amount: sideToAssetToken[Side.Receive].amount.toString(), + feeAmount: takerFee.toString(), + }, + expiration: orderExpiryTimestamp.toString(), + feeRecipient, + salt: orderSalt.toString(), + signature: signatureData, + exchangeContract, + networkId, + }; + return order; + }, + consoleLog(message: string) { + /* tslint:disable */ + console.log(message); + /* tslint:enable */ + }, + async sleepAsync(ms: number) { + return new Promise(resolve => setTimeout(resolve, ms)); + }, + deepEqual(actual: any, expected: any, opts?: { strict: boolean }) { + return deepEqual(actual, expected, opts); + }, + getColSize(items: number) { + const bassCssGridSize = 12; // Source: http://basscss.com/#basscss-grid + const colSize = bassCssGridSize / items; + if (!_.isInteger(colSize)) { + throw new Error(`Number of cols must be divisible by ${bassCssGridSize}`); + } + return colSize; + }, + getScreenWidth() { + const documentEl = document.documentElement; + const body = document.getElementsByTagName('body')[0]; + const widthInPx = window.innerWidth || documentEl.clientWidth || body.clientWidth; + const bodyStyles: any = window.getComputedStyle(document.querySelector('body')); + const widthInEm = widthInPx / parseFloat(bodyStyles['font-size']); - // This logic mirrors the CSS media queries in BassCSS for the `lg-`, `md-` and `sm-` CSS - // class prefixes. Do not edit these. - if (widthInEm > LG_MIN_EM) { - return ScreenWidths.Lg; - } else if (widthInEm > MD_MIN_EM) { - return ScreenWidths.Md; - } else { - return ScreenWidths.Sm; - } - }, - isUserOnMobile(): boolean { - const isUserOnMobile = isMobile(); - return isUserOnMobile; - }, - getEtherScanLinkIfExists(addressOrTxHash: string, networkId: number, suffix: EtherscanLinkSuffixes): string { - const networkName = constants.NETWORK_NAME_BY_ID[networkId]; - if (_.isUndefined(networkName)) { - return undefined; - } - const etherScanPrefix = networkName === Networks.mainnet ? '' : `${networkName.toLowerCase()}.`; - return `https://${etherScanPrefix}etherscan.io/${suffix}/${addressOrTxHash}`; - }, - setUrlHash(anchorId: string) { - window.location.hash = anchorId; - }, - async isU2FSupportedAsync(): Promise { - const w = window as any; - return new Promise((resolve: (isSupported: boolean) => void) => { - if (w.u2f && !w.u2f.getApiVersion) { - // u2f object was found (Firefox with extension) - resolve(true); - } else { - // u2f object was not found. Using Google polyfill - // HACK: u2f.getApiVersion will simply not return a version if the - // U2F call fails for any reason. Because of this, we set a hard 3sec - // timeout to the request on our end. - const getApiVersionTimeoutMs = 3000; - const intervalId = setTimeout(() => { - resolve(false); - }, getApiVersionTimeoutMs); - u2f.getApiVersion((version: number) => { - clearTimeout(intervalId); - resolve(true); - }); - } - }); - }, - // This checks the error message returned from an injected Web3 instance on the page - // after a user was prompted to sign a message or send a transaction and decided to - // reject the request. - didUserDenyWeb3Request(errMsg: string) { - const metamaskDenialErrMsg = 'User denied message'; - const paritySignerDenialErrMsg = 'Request has been rejected'; - const ledgerDenialErrMsg = 'Invalid status 6985'; - const isUserDeniedErrMsg = - _.includes(errMsg, metamaskDenialErrMsg) || - _.includes(errMsg, paritySignerDenialErrMsg) || - _.includes(errMsg, ledgerDenialErrMsg); - return isUserDeniedErrMsg; - }, - getCurrentEnvironment() { - switch (location.host) { - case configs.DOMAIN_DEVELOPMENT: - return 'development'; - case configs.DOMAIN_STAGING: - return 'staging'; - case configs.DOMAIN_PRODUCTION: - return 'production'; - default: - return 'production'; - } - }, - getIdFromName(name: string) { - const id = name.replace(/ /g, '-'); - return id; - }, - getAddressBeginAndEnd(address: string): string { - const truncatedAddress = `${address.substring(0, 6)}...${address.substr(-4)}`; // 0x3d5a...b287 - return truncatedAddress; - }, - hasUniqueNameAndSymbol(tokens: Token[], token: Token) { - if (token.isRegistered) { - return true; // Since it's registered, it is the canonical token - } - const registeredTokens = _.filter(tokens, t => t.isRegistered); - const tokenWithSameNameIfExists = _.find(registeredTokens, { - name: token.name, - }); - const isUniqueName = _.isUndefined(tokenWithSameNameIfExists); - const tokenWithSameSymbolIfExists = _.find(registeredTokens, { - name: token.symbol, - }); - const isUniqueSymbol = _.isUndefined(tokenWithSameSymbolIfExists); - return isUniqueName && isUniqueSymbol; - }, - zeroExErrToHumanReadableErrMsg(error: ZeroExError | ExchangeContractErrs, takerAddress: string): string { - const ZeroExErrorToHumanReadableError: { [error: string]: string } = { - [ZeroExError.ExchangeContractDoesNotExist]: 'Exchange contract does not exist', - [ZeroExError.EtherTokenContractDoesNotExist]: 'EtherToken contract does not exist', - [ZeroExError.TokenTransferProxyContractDoesNotExist]: 'TokenTransferProxy contract does not exist', - [ZeroExError.TokenRegistryContractDoesNotExist]: 'TokenRegistry contract does not exist', - [ZeroExError.TokenContractDoesNotExist]: 'Token contract does not exist', - [ZeroExError.ZRXContractDoesNotExist]: 'ZRX contract does not exist', - [ZeroExError.UnhandledError]: 'Unhandled error occured', - [ZeroExError.UserHasNoAssociatedAddress]: 'User has no addresses available', - [ZeroExError.InvalidSignature]: 'Order signature is not valid', - [ZeroExError.ContractNotDeployedOnNetwork]: 'Contract is not deployed on the detected network', - [ZeroExError.InvalidJump]: 'Invalid jump occured while executing the transaction', - [ZeroExError.OutOfGas]: 'Transaction ran out of gas', - [ZeroExError.NoNetworkId]: 'No network id detected', - }; - const exchangeContractErrorToHumanReadableError: { - [error: string]: string; - } = { - [ExchangeContractErrs.OrderFillExpired]: 'This order has expired', - [ExchangeContractErrs.OrderCancelExpired]: 'This order has expired', - [ExchangeContractErrs.OrderCancelAmountZero]: "Order cancel amount can't be 0", - [ExchangeContractErrs.OrderAlreadyCancelledOrFilled]: - 'This order has already been completely filled or cancelled', - [ExchangeContractErrs.OrderFillAmountZero]: "Order fill amount can't be 0", - [ExchangeContractErrs.OrderRemainingFillAmountZero]: - 'This order has already been completely filled or cancelled', - [ExchangeContractErrs.OrderFillRoundingError]: 'Rounding error will occur when filling this order', - [ExchangeContractErrs.InsufficientTakerBalance]: - 'Taker no longer has a sufficient balance to complete this order', - [ExchangeContractErrs.InsufficientTakerAllowance]: - 'Taker no longer has a sufficient allowance to complete this order', - [ExchangeContractErrs.InsufficientMakerBalance]: - 'Maker no longer has a sufficient balance to complete this order', - [ExchangeContractErrs.InsufficientMakerAllowance]: - 'Maker no longer has a sufficient allowance to complete this order', - [ExchangeContractErrs.InsufficientTakerFeeBalance]: 'Taker no longer has a sufficient balance to pay fees', - [ExchangeContractErrs.InsufficientTakerFeeAllowance]: - 'Taker no longer has a sufficient allowance to pay fees', - [ExchangeContractErrs.InsufficientMakerFeeBalance]: 'Maker no longer has a sufficient balance to pay fees', - [ExchangeContractErrs.InsufficientMakerFeeAllowance]: - 'Maker no longer has a sufficient allowance to pay fees', - [ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker]: `This order can only be filled by ${takerAddress}`, - [ExchangeContractErrs.InsufficientRemainingFillAmount]: 'Insufficient remaining fill amount', - }; - const humanReadableErrorMsg = - exchangeContractErrorToHumanReadableError[error] || ZeroExErrorToHumanReadableError[error]; - return humanReadableErrorMsg; - }, + // This logic mirrors the CSS media queries in BassCSS for the `lg-`, `md-` and `sm-` CSS + // class prefixes. Do not edit these. + if (widthInEm > LG_MIN_EM) { + return ScreenWidths.Lg; + } else if (widthInEm > MD_MIN_EM) { + return ScreenWidths.Md; + } else { + return ScreenWidths.Sm; + } + }, + isUserOnMobile(): boolean { + const isUserOnMobile = isMobile(); + return isUserOnMobile; + }, + getEtherScanLinkIfExists(addressOrTxHash: string, networkId: number, suffix: EtherscanLinkSuffixes): string { + const networkName = constants.NETWORK_NAME_BY_ID[networkId]; + if (_.isUndefined(networkName)) { + return undefined; + } + const etherScanPrefix = networkName === Networks.mainnet ? '' : `${networkName.toLowerCase()}.`; + return `https://${etherScanPrefix}etherscan.io/${suffix}/${addressOrTxHash}`; + }, + setUrlHash(anchorId: string) { + window.location.hash = anchorId; + }, + async isU2FSupportedAsync(): Promise { + const w = window as any; + return new Promise((resolve: (isSupported: boolean) => void) => { + if (w.u2f && !w.u2f.getApiVersion) { + // u2f object was found (Firefox with extension) + resolve(true); + } else { + // u2f object was not found. Using Google polyfill + // HACK: u2f.getApiVersion will simply not return a version if the + // U2F call fails for any reason. Because of this, we set a hard 3sec + // timeout to the request on our end. + const getApiVersionTimeoutMs = 3000; + const intervalId = setTimeout(() => { + resolve(false); + }, getApiVersionTimeoutMs); + u2f.getApiVersion((version: number) => { + clearTimeout(intervalId); + resolve(true); + }); + } + }); + }, + // This checks the error message returned from an injected Web3 instance on the page + // after a user was prompted to sign a message or send a transaction and decided to + // reject the request. + didUserDenyWeb3Request(errMsg: string) { + const metamaskDenialErrMsg = 'User denied message'; + const paritySignerDenialErrMsg = 'Request has been rejected'; + const ledgerDenialErrMsg = 'Invalid status 6985'; + const isUserDeniedErrMsg = + _.includes(errMsg, metamaskDenialErrMsg) || + _.includes(errMsg, paritySignerDenialErrMsg) || + _.includes(errMsg, ledgerDenialErrMsg); + return isUserDeniedErrMsg; + }, + getCurrentEnvironment() { + switch (location.host) { + case configs.DOMAIN_DEVELOPMENT: + return 'development'; + case configs.DOMAIN_STAGING: + return 'staging'; + case configs.DOMAIN_PRODUCTION: + return 'production'; + default: + return 'production'; + } + }, + getIdFromName(name: string) { + const id = name.replace(/ /g, '-'); + return id; + }, + getAddressBeginAndEnd(address: string): string { + const truncatedAddress = `${address.substring(0, 6)}...${address.substr(-4)}`; // 0x3d5a...b287 + return truncatedAddress; + }, + hasUniqueNameAndSymbol(tokens: Token[], token: Token) { + if (token.isRegistered) { + return true; // Since it's registered, it is the canonical token + } + const registeredTokens = _.filter(tokens, t => t.isRegistered); + const tokenWithSameNameIfExists = _.find(registeredTokens, { + name: token.name, + }); + const isUniqueName = _.isUndefined(tokenWithSameNameIfExists); + const tokenWithSameSymbolIfExists = _.find(registeredTokens, { + name: token.symbol, + }); + const isUniqueSymbol = _.isUndefined(tokenWithSameSymbolIfExists); + return isUniqueName && isUniqueSymbol; + }, + zeroExErrToHumanReadableErrMsg(error: ZeroExError | ExchangeContractErrs, takerAddress: string): string { + const ZeroExErrorToHumanReadableError: { [error: string]: string } = { + [ZeroExError.ExchangeContractDoesNotExist]: 'Exchange contract does not exist', + [ZeroExError.EtherTokenContractDoesNotExist]: 'EtherToken contract does not exist', + [ZeroExError.TokenTransferProxyContractDoesNotExist]: 'TokenTransferProxy contract does not exist', + [ZeroExError.TokenRegistryContractDoesNotExist]: 'TokenRegistry contract does not exist', + [ZeroExError.TokenContractDoesNotExist]: 'Token contract does not exist', + [ZeroExError.ZRXContractDoesNotExist]: 'ZRX contract does not exist', + [ZeroExError.UnhandledError]: 'Unhandled error occured', + [ZeroExError.UserHasNoAssociatedAddress]: 'User has no addresses available', + [ZeroExError.InvalidSignature]: 'Order signature is not valid', + [ZeroExError.ContractNotDeployedOnNetwork]: 'Contract is not deployed on the detected network', + [ZeroExError.InvalidJump]: 'Invalid jump occured while executing the transaction', + [ZeroExError.OutOfGas]: 'Transaction ran out of gas', + [ZeroExError.NoNetworkId]: 'No network id detected', + }; + const exchangeContractErrorToHumanReadableError: { + [error: string]: string; + } = { + [ExchangeContractErrs.OrderFillExpired]: 'This order has expired', + [ExchangeContractErrs.OrderCancelExpired]: 'This order has expired', + [ExchangeContractErrs.OrderCancelAmountZero]: "Order cancel amount can't be 0", + [ExchangeContractErrs.OrderAlreadyCancelledOrFilled]: + 'This order has already been completely filled or cancelled', + [ExchangeContractErrs.OrderFillAmountZero]: "Order fill amount can't be 0", + [ExchangeContractErrs.OrderRemainingFillAmountZero]: + 'This order has already been completely filled or cancelled', + [ExchangeContractErrs.OrderFillRoundingError]: 'Rounding error will occur when filling this order', + [ExchangeContractErrs.InsufficientTakerBalance]: + 'Taker no longer has a sufficient balance to complete this order', + [ExchangeContractErrs.InsufficientTakerAllowance]: + 'Taker no longer has a sufficient allowance to complete this order', + [ExchangeContractErrs.InsufficientMakerBalance]: + 'Maker no longer has a sufficient balance to complete this order', + [ExchangeContractErrs.InsufficientMakerAllowance]: + 'Maker no longer has a sufficient allowance to complete this order', + [ExchangeContractErrs.InsufficientTakerFeeBalance]: 'Taker no longer has a sufficient balance to pay fees', + [ExchangeContractErrs.InsufficientTakerFeeAllowance]: + 'Taker no longer has a sufficient allowance to pay fees', + [ExchangeContractErrs.InsufficientMakerFeeBalance]: 'Maker no longer has a sufficient balance to pay fees', + [ExchangeContractErrs.InsufficientMakerFeeAllowance]: + 'Maker no longer has a sufficient allowance to pay fees', + [ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker]: `This order can only be filled by ${takerAddress}`, + [ExchangeContractErrs.InsufficientRemainingFillAmount]: 'Insufficient remaining fill amount', + }; + const humanReadableErrorMsg = + exchangeContractErrorToHumanReadableError[error] || ZeroExErrorToHumanReadableError[error]; + return humanReadableErrorMsg; + }, }; diff --git a/packages/website/ts/web3_wrapper.ts b/packages/website/ts/web3_wrapper.ts index 0cd2a5cc2..415df6e8b 100644 --- a/packages/website/ts/web3_wrapper.ts +++ b/packages/website/ts/web3_wrapper.ts @@ -5,154 +5,154 @@ import { utils } from 'ts/utils/utils'; import * as Web3 from 'web3'; export class Web3Wrapper { - private _dispatcher: Dispatcher; - private _web3: Web3; - private _prevNetworkId: number; - private _shouldPollUserAddress: boolean; - private _watchNetworkAndBalanceIntervalId: NodeJS.Timer; - private _prevUserEtherBalanceInEth: BigNumber; - private _prevUserAddress: string; - constructor( - dispatcher: Dispatcher, - provider: Web3.Provider, - networkIdIfExists: number, - shouldPollUserAddress: boolean, - ) { - this._dispatcher = dispatcher; - this._prevNetworkId = networkIdIfExists; - this._shouldPollUserAddress = shouldPollUserAddress; + private _dispatcher: Dispatcher; + private _web3: Web3; + private _prevNetworkId: number; + private _shouldPollUserAddress: boolean; + private _watchNetworkAndBalanceIntervalId: NodeJS.Timer; + private _prevUserEtherBalanceInEth: BigNumber; + private _prevUserAddress: string; + constructor( + dispatcher: Dispatcher, + provider: Web3.Provider, + networkIdIfExists: number, + shouldPollUserAddress: boolean, + ) { + this._dispatcher = dispatcher; + this._prevNetworkId = networkIdIfExists; + this._shouldPollUserAddress = shouldPollUserAddress; - this._web3 = new Web3(); - this._web3.setProvider(provider); + this._web3 = new Web3(); + this._web3.setProvider(provider); - // tslint:disable-next-line:no-floating-promises - this._startEmittingNetworkConnectionAndUserBalanceStateAsync(); - } - public isAddress(address: string) { - return this._web3.isAddress(address); - } - public async getAccountsAsync(): Promise { - const addresses = await promisify(this._web3.eth.getAccounts)(); - return addresses; - } - public async getFirstAccountIfExistsAsync() { - const addresses = await this.getAccountsAsync(); - if (_.isEmpty(addresses)) { - return ''; - } - return addresses[0]; - } - public async getNodeVersionAsync(): Promise { - const nodeVersion = await promisify(this._web3.version.getNode)(); - return nodeVersion; - } - public getProviderObj() { - return this._web3.currentProvider; - } - public async getNetworkIdIfExists() { - try { - const networkId = await this._getNetworkAsync(); - return Number(networkId); - } catch (err) { - return undefined; - } - } - public async getBalanceInEthAsync(owner: string): Promise { - const balanceInWei: BigNumber = await promisify(this._web3.eth.getBalance)(owner); - const balanceEthOldBigNumber = this._web3.fromWei(balanceInWei, 'ether'); - const balanceEth = new BigNumber(balanceEthOldBigNumber); - return balanceEth; - } - public async doesContractExistAtAddressAsync(address: string): Promise { - const code = await promisify(this._web3.eth.getCode)(address); - // Regex matches 0x0, 0x00, 0x in order to accomodate poorly implemented clients - const zeroHexAddressRegex = /^0[xX][0]*$/; - const didFindCode = _.isNull(code.match(zeroHexAddressRegex)); - return didFindCode; - } - public async signTransactionAsync(address: string, message: string): Promise { - const signData = await promisify(this._web3.eth.sign)(address, message); - return signData; - } - public async getBlockTimestampAsync(blockHash: string): Promise { - const { timestamp } = await promisify(this._web3.eth.getBlock)(blockHash); - return timestamp; - } - public destroy() { - this._stopEmittingNetworkConnectionAndUserBalanceStateAsync(); - // HACK: stop() is only available on providerEngine instances - const provider = this._web3.currentProvider; - if (!_.isUndefined((provider as any).stop)) { - (provider as any).stop(); - } - } - // This should only be called from the LedgerConfigDialog - public updatePrevUserAddress(userAddress: string) { - this._prevUserAddress = userAddress; - } - private async _getNetworkAsync() { - const networkId = await promisify(this._web3.version.getNetwork)(); - return networkId; - } - private async _startEmittingNetworkConnectionAndUserBalanceStateAsync() { - if (!_.isUndefined(this._watchNetworkAndBalanceIntervalId)) { - return; // we are already emitting the state - } + // tslint:disable-next-line:no-floating-promises + this._startEmittingNetworkConnectionAndUserBalanceStateAsync(); + } + public isAddress(address: string) { + return this._web3.isAddress(address); + } + public async getAccountsAsync(): Promise { + const addresses = await promisify(this._web3.eth.getAccounts)(); + return addresses; + } + public async getFirstAccountIfExistsAsync() { + const addresses = await this.getAccountsAsync(); + if (_.isEmpty(addresses)) { + return ''; + } + return addresses[0]; + } + public async getNodeVersionAsync(): Promise { + const nodeVersion = await promisify(this._web3.version.getNode)(); + return nodeVersion; + } + public getProviderObj() { + return this._web3.currentProvider; + } + public async getNetworkIdIfExists() { + try { + const networkId = await this._getNetworkAsync(); + return Number(networkId); + } catch (err) { + return undefined; + } + } + public async getBalanceInEthAsync(owner: string): Promise { + const balanceInWei: BigNumber = await promisify(this._web3.eth.getBalance)(owner); + const balanceEthOldBigNumber = this._web3.fromWei(balanceInWei, 'ether'); + const balanceEth = new BigNumber(balanceEthOldBigNumber); + return balanceEth; + } + public async doesContractExistAtAddressAsync(address: string): Promise { + const code = await promisify(this._web3.eth.getCode)(address); + // Regex matches 0x0, 0x00, 0x in order to accomodate poorly implemented clients + const zeroHexAddressRegex = /^0[xX][0]*$/; + const didFindCode = _.isNull(code.match(zeroHexAddressRegex)); + return didFindCode; + } + public async signTransactionAsync(address: string, message: string): Promise { + const signData = await promisify(this._web3.eth.sign)(address, message); + return signData; + } + public async getBlockTimestampAsync(blockHash: string): Promise { + const { timestamp } = await promisify(this._web3.eth.getBlock)(blockHash); + return timestamp; + } + public destroy() { + this._stopEmittingNetworkConnectionAndUserBalanceStateAsync(); + // HACK: stop() is only available on providerEngine instances + const provider = this._web3.currentProvider; + if (!_.isUndefined((provider as any).stop)) { + (provider as any).stop(); + } + } + // This should only be called from the LedgerConfigDialog + public updatePrevUserAddress(userAddress: string) { + this._prevUserAddress = userAddress; + } + private async _getNetworkAsync() { + const networkId = await promisify(this._web3.version.getNetwork)(); + return networkId; + } + private async _startEmittingNetworkConnectionAndUserBalanceStateAsync() { + if (!_.isUndefined(this._watchNetworkAndBalanceIntervalId)) { + return; // we are already emitting the state + } - let prevNodeVersion: string; - this._prevUserEtherBalanceInEth = new BigNumber(0); - this._dispatcher.updateNetworkId(this._prevNetworkId); - this._watchNetworkAndBalanceIntervalId = intervalUtils.setAsyncExcludingInterval( - async () => { - // Check for network state changes - const currentNetworkId = await this.getNetworkIdIfExists(); - if (currentNetworkId !== this._prevNetworkId) { - this._prevNetworkId = currentNetworkId; - this._dispatcher.updateNetworkId(currentNetworkId); - } + let prevNodeVersion: string; + this._prevUserEtherBalanceInEth = new BigNumber(0); + this._dispatcher.updateNetworkId(this._prevNetworkId); + this._watchNetworkAndBalanceIntervalId = intervalUtils.setAsyncExcludingInterval( + async () => { + // Check for network state changes + const currentNetworkId = await this.getNetworkIdIfExists(); + if (currentNetworkId !== this._prevNetworkId) { + this._prevNetworkId = currentNetworkId; + this._dispatcher.updateNetworkId(currentNetworkId); + } - // Check for node version changes - const currentNodeVersion = await this.getNodeVersionAsync(); - if (currentNodeVersion !== prevNodeVersion) { - prevNodeVersion = currentNodeVersion; - this._dispatcher.updateNodeVersion(currentNodeVersion); - } + // Check for node version changes + const currentNodeVersion = await this.getNodeVersionAsync(); + if (currentNodeVersion !== prevNodeVersion) { + prevNodeVersion = currentNodeVersion; + this._dispatcher.updateNodeVersion(currentNodeVersion); + } - if (this._shouldPollUserAddress) { - const userAddressIfExists = await this.getFirstAccountIfExistsAsync(); - // Update makerAddress on network change - if (this._prevUserAddress !== userAddressIfExists) { - this._prevUserAddress = userAddressIfExists; - this._dispatcher.updateUserAddress(userAddressIfExists); - } + if (this._shouldPollUserAddress) { + const userAddressIfExists = await this.getFirstAccountIfExistsAsync(); + // Update makerAddress on network change + if (this._prevUserAddress !== userAddressIfExists) { + this._prevUserAddress = userAddressIfExists; + this._dispatcher.updateUserAddress(userAddressIfExists); + } - // Check for user ether balance changes - if (userAddressIfExists !== '') { - await this._updateUserEtherBalanceAsync(userAddressIfExists); - } - } else { - // This logic is primarily for the Ledger, since we don't regularly poll for the address - // we simply update the balance for the last fetched address. - if (!_.isEmpty(this._prevUserAddress)) { - await this._updateUserEtherBalanceAsync(this._prevUserAddress); - } - } - }, - 5000, - (err: Error) => { - utils.consoleLog(`Watching network and balances failed: ${err}`); - this._stopEmittingNetworkConnectionAndUserBalanceStateAsync(); - }, - ); - } - private async _updateUserEtherBalanceAsync(userAddress: string) { - const balance = await this.getBalanceInEthAsync(userAddress); - if (!balance.eq(this._prevUserEtherBalanceInEth)) { - this._prevUserEtherBalanceInEth = balance; - this._dispatcher.updateUserEtherBalance(balance); - } - } - private _stopEmittingNetworkConnectionAndUserBalanceStateAsync() { - intervalUtils.clearAsyncExcludingInterval(this._watchNetworkAndBalanceIntervalId); - } + // Check for user ether balance changes + if (userAddressIfExists !== '') { + await this._updateUserEtherBalanceAsync(userAddressIfExists); + } + } else { + // This logic is primarily for the Ledger, since we don't regularly poll for the address + // we simply update the balance for the last fetched address. + if (!_.isEmpty(this._prevUserAddress)) { + await this._updateUserEtherBalanceAsync(this._prevUserAddress); + } + } + }, + 5000, + (err: Error) => { + utils.consoleLog(`Watching network and balances failed: ${err}`); + this._stopEmittingNetworkConnectionAndUserBalanceStateAsync(); + }, + ); + } + private async _updateUserEtherBalanceAsync(userAddress: string) { + const balance = await this.getBalanceInEthAsync(userAddress); + if (!balance.eq(this._prevUserEtherBalanceInEth)) { + this._prevUserEtherBalanceInEth = balance; + this._dispatcher.updateUserEtherBalance(balance); + } + } + private _stopEmittingNetworkConnectionAndUserBalanceStateAsync() { + intervalUtils.clearAsyncExcludingInterval(this._watchNetworkAndBalanceIntervalId); + } } -- cgit v1.2.3