diff options
author | Francesco Agosti <francesco.agosti93@gmail.com> | 2018-07-22 18:45:21 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-07-22 18:45:21 +0800 |
commit | 0e72b67865ce7f891283ed9d21bd36cfaab4e645 (patch) | |
tree | 653d1f39f5819e7136f6b673ae75015c09a7aae0 /packages | |
parent | b987eebf215391a8af9cff995c7f6c4a6e434bd9 (diff) | |
parent | ff12aafc0fa30cfbd1c59f9a2fc3491395e2cdab (diff) | |
download | dexon-0x-contracts-0e72b67865ce7f891283ed9d21bd36cfaab4e645.tar.gz dexon-0x-contracts-0e72b67865ce7f891283ed9d21bd36cfaab4e645.tar.zst dexon-0x-contracts-0e72b67865ce7f891283ed9d21bd36cfaab4e645.zip |
Merge pull request #895 from 0xProject/feature/website/send-eth-from-portal
Send Ether from Portal
Diffstat (limited to 'packages')
-rw-r--r-- | packages/website/ts/blockchain.ts | 33 | ||||
-rw-r--r-- | packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx | 6 | ||||
-rw-r--r-- | packages/website/ts/components/dialogs/send_dialog.tsx | 40 | ||||
-rw-r--r-- | packages/website/ts/components/flash_messages/asset_send_completed.tsx (renamed from packages/website/ts/components/flash_messages/token_send_completed.tsx) | 14 | ||||
-rw-r--r-- | packages/website/ts/components/inputs/eth_amount_input.tsx | 3 | ||||
-rw-r--r-- | packages/website/ts/components/send_button.tsx | 14 | ||||
-rw-r--r-- | packages/website/ts/components/token_balances.tsx | 23 | ||||
-rw-r--r-- | packages/website/ts/components/wallet/wrap_ether_item.tsx | 8 | ||||
-rw-r--r-- | packages/website/ts/containers/inputs/eth_amount_input.ts | 36 |
9 files changed, 129 insertions, 48 deletions
diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index 88461f8a9..45994be5f 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -35,7 +35,7 @@ import * as moment from 'moment'; import * as React from 'react'; import contract = require('truffle-contract'); import { BlockchainWatcher } from 'ts/blockchain_watcher'; -import { TokenSendCompleted } from 'ts/components/flash_messages/token_send_completed'; +import { AssetSendCompleted } from 'ts/components/flash_messages/asset_send_completed'; import { TransactionSubmitted } from 'ts/components/flash_messages/transaction_submitted'; import { trackedTokenStorage } from 'ts/local_storage/tracked_token_storage'; import { tradeHistoryStorage } from 'ts/local_storage/trade_history_storage'; @@ -276,6 +276,32 @@ export class Blockchain { ); await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); } + public async sendAsync(toAddress: string, amountInBaseUnits: BigNumber): Promise<void> { + utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); + const transaction = { + from: this._userAddressIfExists, + to: toAddress, + value: amountInBaseUnits, + gasPrice: this._defaultGasPrice, + }; + this._showFlashMessageIfLedger(); + const txHash = await this._web3Wrapper.sendTransactionAsync(transaction); + await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); + const etherScanLinkIfExists = sharedUtils.getEtherScanLinkIfExists( + txHash, + this.networkId, + EtherscanLinkSuffixes.Tx, + ); + this._dispatcher.showFlashMessage( + React.createElement(AssetSendCompleted, { + etherScanLinkIfExists, + toAddress, + amountInBaseUnits, + decimals: constants.DECIMAL_PLACES_ETH, + symbol: constants.ETHER_SYMBOL, + }), + ); + } public async transferAsync(token: Token, toAddress: string, amountInBaseUnits: BigNumber): Promise<void> { utils.assert(!_.isUndefined(this._contractWrappers), 'ContractWrappers must be instantiated.'); utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); @@ -297,11 +323,12 @@ export class Blockchain { EtherscanLinkSuffixes.Tx, ); this._dispatcher.showFlashMessage( - React.createElement(TokenSendCompleted, { + React.createElement(AssetSendCompleted, { etherScanLinkIfExists, - token, toAddress, amountInBaseUnits, + decimals: token.decimals, + symbol: token.symbol, }), ); } 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 7b09cc92c..5f4bf8519 100644 --- a/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx +++ b/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx @@ -1,15 +1,13 @@ import { colors } from '@0xproject/react-shared'; import { BigNumber } from '@0xproject/utils'; -import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as _ from 'lodash'; import Dialog from 'material-ui/Dialog'; import FlatButton from 'material-ui/FlatButton'; import * as React from 'react'; import { Blockchain } from 'ts/blockchain'; -import { EthAmountInput } from 'ts/components/inputs/eth_amount_input'; import { TokenAmountInput } from 'ts/components/inputs/token_amount_input'; +import { EthAmountInput } from 'ts/containers/inputs/eth_amount_input'; import { Side, Token } from 'ts/types'; -import { constants } from 'ts/utils/constants'; interface EthWethConversionDialogProps { blockchain: Blockchain; @@ -78,7 +76,6 @@ export class EthWethConversionDialog extends React.Component< ? '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; - const etherBalanceInEth = Web3Wrapper.toUnitAmount(this.props.etherBalanceInWei, constants.DECIMAL_PLACES_ETH); return ( <div> <div className="pb2">{explanation}</div> @@ -106,7 +103,6 @@ export class EthWethConversionDialog extends React.Component< /> ) : ( <EthAmountInput - balance={etherBalanceInEth} amount={this.state.value} onChange={this._onValueChange.bind(this)} shouldCheckBalance={true} diff --git a/packages/website/ts/components/dialogs/send_dialog.tsx b/packages/website/ts/components/dialogs/send_dialog.tsx index 8a98fdf69..c1179dbd0 100644 --- a/packages/website/ts/components/dialogs/send_dialog.tsx +++ b/packages/website/ts/components/dialogs/send_dialog.tsx @@ -6,6 +6,7 @@ import * as React from 'react'; import { Blockchain } from 'ts/blockchain'; import { AddressInput } from 'ts/components/inputs/address_input'; import { TokenAmountInput } from 'ts/components/inputs/token_amount_input'; +import { EthAmountInput } from 'ts/containers/inputs/eth_amount_input'; import { Token } from 'ts/types'; interface SendDialogProps { @@ -15,7 +16,7 @@ interface SendDialogProps { onComplete: (recipient: string, value: BigNumber) => void; onCancelled: () => void; isOpen: boolean; - token: Token; + asset: Token | 'ETH'; lastForceTokenStateRefetch: number; } @@ -58,23 +59,23 @@ export class SendDialog extends React.Component<SendDialogProps, SendDialogState ); } private _renderSendDialogBody(): React.ReactNode { - return ( - <div className="mx-auto" style={{ maxWidth: 300 }}> - <div style={{ height: 80 }}> - <AddressInput - initialAddress={this.state.recipient} - updateAddress={this._onRecipientChange.bind(this)} - isRequired={true} - label={'Recipient address'} - hintText={'Address'} - /> - </div> + const input = + this.props.asset === 'ETH' ? ( + <EthAmountInput + label="Amount to send" + shouldShowIncompleteErrs={this.state.shouldShowIncompleteErrs} + shouldCheckBalance={true} + shouldShowErrs={true} + onChange={this._onValueChange.bind(this)} + amount={this.state.value} + /> + ) : ( <TokenAmountInput blockchain={this.props.blockchain} userAddress={this.props.userAddress} networkId={this.props.networkId} label="Amount to send" - token={this.props.token} + token={this.props.asset} shouldShowIncompleteErrs={this.state.shouldShowIncompleteErrs} shouldCheckBalance={true} shouldCheckAllowance={false} @@ -82,6 +83,19 @@ export class SendDialog extends React.Component<SendDialogProps, SendDialogState amount={this.state.value} lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch} /> + ); + return ( + <div className="mx-auto" style={{ maxWidth: 300 }}> + <div style={{ height: 80 }}> + <AddressInput + initialAddress={this.state.recipient} + updateAddress={this._onRecipientChange.bind(this)} + isRequired={true} + label="Recipient address'" + hintText="Address" + /> + </div> + {input} </div> ); } diff --git a/packages/website/ts/components/flash_messages/token_send_completed.tsx b/packages/website/ts/components/flash_messages/asset_send_completed.tsx index f3f1ea2fc..f76e05fe1 100644 --- a/packages/website/ts/components/flash_messages/token_send_completed.tsx +++ b/packages/website/ts/components/flash_messages/asset_send_completed.tsx @@ -3,30 +3,30 @@ import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as _ from 'lodash'; import * as React from 'react'; -import { Token } from 'ts/types'; import { utils } from 'ts/utils/utils'; -interface TokenSendCompletedProps { +interface AssetSendCompletedProps { etherScanLinkIfExists?: string; - token: Token; toAddress: string; amountInBaseUnits: BigNumber; + decimals: number; + symbol: string; } -interface TokenSendCompletedState {} +interface AssetSendCompletedState {} -export class TokenSendCompleted extends React.Component<TokenSendCompletedProps, TokenSendCompletedState> { +export class AssetSendCompleted extends React.Component<AssetSendCompletedProps, AssetSendCompletedState> { public render(): React.ReactNode { const etherScanLink = !_.isUndefined(this.props.etherScanLinkIfExists) && ( <a style={{ color: colors.white }} href={`${this.props.etherScanLinkIfExists}`} target="_blank"> Verify on Etherscan </a> ); - const amountInUnits = Web3Wrapper.toUnitAmount(this.props.amountInBaseUnits, this.props.token.decimals); + const amountInUnits = Web3Wrapper.toUnitAmount(this.props.amountInBaseUnits, this.props.decimals); const truncatedAddress = utils.getAddressBeginAndEnd(this.props.toAddress); return ( <div> - {`Sent ${amountInUnits} ${this.props.token.symbol} to ${truncatedAddress}: `} + {`Sent ${amountInUnits} ${this.props.symbol} to ${truncatedAddress}: `} {etherScanLink} </div> ); diff --git a/packages/website/ts/components/inputs/eth_amount_input.tsx b/packages/website/ts/components/inputs/eth_amount_input.tsx index 552d4277a..3a7905442 100644 --- a/packages/website/ts/components/inputs/eth_amount_input.tsx +++ b/packages/website/ts/components/inputs/eth_amount_input.tsx @@ -28,14 +28,13 @@ export class EthAmountInput extends React.Component<EthAmountInputProps, EthAmou public static defaultProps: Partial<EthAmountInputProps> = { shouldShowErrs: true, shouldShowUnderline: true, - style: { height: 63 }, }; public render(): React.ReactNode { const amount = this.props.amount ? Web3Wrapper.toUnitAmount(this.props.amount, constants.DECIMAL_PLACES_ETH) : undefined; return ( - <div className="flex overflow-hidden" style={this.props.style}> + <div className="flex" style={this.props.style}> <BalanceBoundedInput label={this.props.label} balance={this.props.balance} diff --git a/packages/website/ts/components/send_button.tsx b/packages/website/ts/components/send_button.tsx index ac55d430b..107983dee 100644 --- a/packages/website/ts/components/send_button.tsx +++ b/packages/website/ts/components/send_button.tsx @@ -12,7 +12,7 @@ import { utils } from 'ts/utils/utils'; interface SendButtonProps { userAddress: string; networkId: number; - token: Token; + asset: Token | 'ETH'; dispatcher: Dispatcher; blockchain: Blockchain; onError: () => void; @@ -51,7 +51,7 @@ export class SendButton extends React.Component<SendButtonProps, SendButtonState isOpen={this.state.isSendDialogVisible} onComplete={this._onSendAmountSelectedAsync.bind(this)} onCancelled={this._toggleSendDialog.bind(this)} - token={this.props.token} + asset={this.props.asset} lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch} /> </div> @@ -67,10 +67,14 @@ export class SendButton extends React.Component<SendButtonProps, SendButtonState isSending: true, }); this._toggleSendDialog(); - const token = this.props.token; try { - await this.props.blockchain.transferAsync(token, recipient, value); - await this.props.refetchTokenStateAsync(token.address); + if (this.props.asset === 'ETH') { + await this.props.blockchain.sendAsync(recipient, value); + } else { + const token = this.props.asset; + await this.props.blockchain.transferAsync(token, recipient, value); + await this.props.refetchTokenStateAsync(token.address); + } } catch (err) { const errMsg = `${err}`; if (_.includes(errMsg, BlockchainCallErrs.UserHasNoAssociatedAddresses)) { diff --git a/packages/website/ts/components/token_balances.tsx b/packages/website/ts/components/token_balances.tsx index c8d80702b..550438e76 100644 --- a/packages/website/ts/components/token_balances.tsx +++ b/packages/website/ts/components/token_balances.tsx @@ -204,11 +204,8 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala <TableHeaderColumn>Currency</TableHeaderColumn> <TableHeaderColumn>Balance</TableHeaderColumn> <TableRowColumn className="sm-hide xs-hide" style={stubColumnStyle} /> - {isTestNetwork && ( - <TableHeaderColumn style={{ paddingLeft: 3 }}> - {isSmallScreen ? 'Faucet' : 'Request from faucet'} - </TableHeaderColumn> - )} + {isTestNetwork && <TableHeaderColumn style={{ paddingLeft: 3 }}>Action</TableHeaderColumn>} + <TableHeaderColumn>Send</TableHeaderColumn> </TableRow> </TableHeader> <TableBody displayRowCheckbox={false}> @@ -235,6 +232,20 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala /> </TableRowColumn> )} + <TableRowColumn> + <SendButton + userAddress={this.props.userAddress} + networkId={this.props.networkId} + blockchain={this.props.blockchain} + dispatcher={this.props.dispatcher} + asset="ETH" + onError={this._onSendFailed.bind(this)} + lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch} + // This is not necessary for ETH. + // tslint:disable:jsx-no-lambda + refetchTokenStateAsync={() => undefined} + /> + </TableRowColumn> </TableRow> </TableBody> </Table> @@ -402,7 +413,7 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala networkId={this.props.networkId} blockchain={this.props.blockchain} dispatcher={this.props.dispatcher} - token={token} + asset={token} onError={this._onSendFailed.bind(this)} lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch} refetchTokenStateAsync={this._refetchTokenStateAsync.bind(this, token.address)} diff --git a/packages/website/ts/components/wallet/wrap_ether_item.tsx b/packages/website/ts/components/wallet/wrap_ether_item.tsx index fcab5d1dd..54ace7ae2 100644 --- a/packages/website/ts/components/wallet/wrap_ether_item.tsx +++ b/packages/website/ts/components/wallet/wrap_ether_item.tsx @@ -6,9 +6,9 @@ import FlatButton from 'material-ui/FlatButton'; import * as React from 'react'; import { Blockchain } from 'ts/blockchain'; -import { EthAmountInput } from 'ts/components/inputs/eth_amount_input'; import { TokenAmountInput } from 'ts/components/inputs/token_amount_input'; import { Container } from 'ts/components/ui/container'; +import { EthAmountInput } from 'ts/containers/inputs/eth_amount_input'; import { Dispatcher } from 'ts/redux/dispatcher'; import { colors } from 'ts/style/colors'; import { BlockchainCallErrs, Side, Token } from 'ts/types'; @@ -87,13 +87,8 @@ export class WrapEtherItem extends React.Component<WrapEtherItemProps, WrapEther }; } public render(): React.ReactNode { - const etherBalanceInEth = Web3Wrapper.toUnitAmount( - this.props.userEtherBalanceInWei, - constants.DECIMAL_PLACES_ETH, - ); const isWrappingEth = this.props.direction === Side.Deposit; const topLabelText = isWrappingEth ? 'Convert ETH into WETH 1:1' : 'Convert WETH into ETH 1:1'; - return ( <Container className="flex" backgroundColor={colors.walletFocusedItemBackground} paddingTop="25px"> <div>{this._renderIsEthConversionHappeningSpinner()} </div> @@ -103,7 +98,6 @@ export class WrapEtherItem extends React.Component<WrapEtherItemProps, WrapEther <div style={styles.inputContainer}> {isWrappingEth ? ( <EthAmountInput - balance={etherBalanceInEth} amount={this.state.currentInputAmount} hintText="0.00" onChange={this._onValueChange.bind(this)} diff --git a/packages/website/ts/containers/inputs/eth_amount_input.ts b/packages/website/ts/containers/inputs/eth_amount_input.ts new file mode 100644 index 000000000..9ef903b55 --- /dev/null +++ b/packages/website/ts/containers/inputs/eth_amount_input.ts @@ -0,0 +1,36 @@ +import { BigNumber } from '@0xproject/utils'; +import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import * as React from 'react'; +import { connect } from 'react-redux'; +import { State } from 'ts/redux/reducer'; +import { ValidatedBigNumberCallback } from 'ts/types'; +import { constants } from 'ts/utils/constants'; + +import { EthAmountInput as EthAmountInputComponent } from 'ts/components/inputs/eth_amount_input'; + +interface EthAmountInputProps { + label?: string; + amount?: BigNumber; + hintText?: string; + onChange: ValidatedBigNumberCallback; + onErrorMsgChange?: (errorMsg: React.ReactNode) => void; + shouldShowIncompleteErrs: boolean; + shouldCheckBalance: boolean; + shouldShowErrs?: boolean; + shouldShowUnderline?: boolean; + style?: React.CSSProperties; + labelStyle?: React.CSSProperties; + inputHintStyle?: React.CSSProperties; +} + +interface ConnectedState { + balance: BigNumber; +} + +const mapStateToProps = (state: State, _ownProps: EthAmountInputProps): ConnectedState => ({ + balance: Web3Wrapper.toUnitAmount(state.userEtherBalanceInWei, constants.DECIMAL_PLACES_ETH), +}); + +export const EthAmountInput: React.ComponentClass<EthAmountInputProps> = connect(mapStateToProps)( + EthAmountInputComponent, +); |