const inherits = require('util').inherits const PersistentForm = require('../lib/persistent-form') const h = require('react-hyperscript') const connect = require('react-redux').connect const Identicon = require('./components/identicon') const actions = require('../../ui/app/actions') const util = require('./util') const numericBalance = require('./util').numericBalance const addressSummary = require('./util').addressSummary const isHex = require('./util').isHex const EthBalance = require('./components/eth-balance') const EnsInput = require('./components/ens-input') const ethUtil = require('ethereumjs-util') module.exports = connect(mapStateToProps)(SendTransactionScreen) function mapStateToProps (state) { var result = { address: state.metamask.selectedAddress, accounts: state.metamask.accounts, identities: state.metamask.identities, warning: state.appState.warning, network: state.metamask.network, addressBook: state.metamask.addressBook, conversionRate: state.metamask.conversionRate, currentCurrency: state.metamask.currentCurrency, } result.error = result.warning && result.warning.split('.')[0] result.account = result.accounts[result.address] result.identity = result.identities[result.address] result.balance = result.account ? numericBalance(result.account.balance) : null return result } inherits(SendTransactionScreen, PersistentForm) function SendTransactionScreen () { PersistentForm.call(this) } SendTransactionScreen.prototype.render = function () { this.persistentFormParentId = 'send-tx-form' const props = this.props const { address, account, identity, network, identities, addressBook, conversionRate, currentCurrency, } = props return ( h('.send-screen.flex-column.flex-grow', [ // // Sender Profile // h('.account-data-subsection.flex-row.flex-grow', { style: { margin: '0 20px', }, }, [ // header - identicon + nav h('.flex-row.flex-space-between', { style: { marginTop: '15px', }, }, [ // back button h('i.fa.fa-arrow-left.fa-lg.cursor-pointer.color-orange', { onClick: this.back.bind(this), }), // large identicon h('.identicon-wrapper.flex-column.flex-center.select-none', [ h(Identicon, { diameter: 62, address: address, }), ]), // invisible place holder h('i.fa.fa-users.fa-lg.invisible', { style: { marginTop: '28px', }, }), ]), // account label h('.flex-column', { style: { marginTop: '10px', alignItems: 'flex-start', }, }, [ h('h2.font-medium.color-forest.flex-center', { style: { paddingTop: '8px', marginBottom: '8px', }, }, identity && identity.name), // address and getter actions h('.flex-row.flex-center', { style: { marginBottom: '8px', }, }, [ h('div', { style: { lineHeight: '16px', }, }, addressSummary(address)), ]), // balance h('.flex-row.flex-center', [ h(EthBalance, { value: account && account.balance, conversionRate, currentCurrency, }), ]), ]), ]), // // Required Fields // h('h3.flex-center.text-transform-uppercase', { style: { background: '#EBEBEB', color: '#AEAEAE', marginTop: '15px', marginBottom: '16px', }, }, [ 'Send Transaction', ]), // error message props.error && h('span.error.flex-center', props.error), // 'to' field h('section.flex-row.flex-center', [ h(EnsInput, { name: 'address', placeholder: 'Recipient Address', onChange: this.recipientDidChange.bind(this), network, identities, addressBook, }), ]), // 'amount' and send button h('section.flex-row.flex-center', [ h('input.large-input', { name: 'amount', placeholder: 'Amount', type: 'number', style: { marginRight: '6px', }, dataset: { persistentFormId: 'tx-amount', }, }), h('button.primary', { onClick: this.onSubmit.bind(this), style: { textTransform: 'uppercase', }, }, 'Next'), ]), // // Optional Fields // h('h3.flex-center.text-transform-uppercase', { style: { background: '#EBEBEB', color: '#AEAEAE', marginTop: '16px', marginBottom: '16px', }, }, [ 'Transaction Data (optional)', ]), // 'data' field h('section.flex-column.flex-center', [ h('input.large-input', { name: 'txData', placeholder: '0x01234', style: { width: '100%', resize: 'none', }, dataset: { persistentFormId: 'tx-data', }, }), ]), ]) ) } SendTransactionScreen.prototype.navigateToAccounts = function (event) { event.stopPropagation() this.props.dispatch(actions.showAccountsPage()) } SendTransactionScreen.prototype.back = function () { var address = this.props.address this.props.dispatch(actions.backToAccountDetail(address)) } SendTransactionScreen.prototype.recipientDidChange = function (recipient, nickname) { this.setState({ recipient: recipient, nickname: nickname, }) } SendTransactionScreen.prototype.onSubmit = function () { const state = this.state || {} const recipient = state.recipient || document.querySelector('input[name="address"]').value.replace(/^[.\s]+|[.\s]+$/g, '') const nickname = state.nickname || ' ' const input = document.querySelector('input[name="amount"]').value const parts = input.split('') let message if (isNaN(input) || input === '') { message = 'Invalid ether value.' return this.props.dispatch(actions.displayWarning(message)) } if (parts[1]) { var decimal = parts[1] if (decimal.length > 18) { message = 'Ether amount is too precise.' return this.props.dispatch(actions.displayWarning(message)) } } const value = util.normalizeEthStringToWei(input) const txData = document.querySelector('input[name="txData"]').value const balance = this.props.balance if (value.gt(balance)) { message = 'Insufficient funds.' return this.props.dispatch(actions.displayWarning(message)) } if (input < 0) { message = 'Can not send negative amounts of ETH.' return this.props.dispatch(actions.displayWarning(message)) } if ((util.isInvalidChecksumAddress(recipient))) { message = 'Recipient address checksum is invalid.' return this.props.dispatch(actions.displayWarning(message)) } if ((!util.isValidAddress(recipient) && !txData) || (!recipient && !txData)) { message = 'Recipient address is invalid.' return this.props.dispatch(actions.displayWarning(message)) } if (!isHex(ethUtil.stripHexPrefix(txData)) && txData) { message = 'Transaction data must be hex string.' return this.props.dispatch(actions.displayWarning(message)) } this.props.dispatch(actions.hideWarning()) this.props.dispatch(actions.addToAddressBook(recipient, nickname)) var txParams = { from: this.props.address, value: '0x' + value.toString(16), } if (recipient) txParams.to = ethUtil.addHexPrefix(recipient) if (txData) txParams.data = txData this.props.dispatch(actions.signTx(txParams)) }