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