|
|
|
// const { inherits } = require('util')
|
|
|
|
// const PersistentForm = require('../lib/persistent-form')
|
|
|
|
// const h = require('react-hyperscript')
|
|
|
|
// const connect = require('react-redux').connect
|
|
|
|
// const Identicon = require('./components/identicon')
|
|
|
|
// const EnsInput = require('./components/ens-input')
|
|
|
|
// const GasTooltip = require('./components/send/gas-tooltip')
|
|
|
|
// const CurrencyToggle = require('./components/send/currency-toggle')
|
|
|
|
// const GasFeeDisplay = require('./components/send/gas-fee-display')
|
|
|
|
// const { getSelectedIdentity } = require('./selectors')
|
|
|
|
|
|
|
|
// const {
|
|
|
|
// showAccountsPage,
|
|
|
|
// backToAccountDetail,
|
|
|
|
// displayWarning,
|
|
|
|
// hideWarning,
|
|
|
|
// addToAddressBook,
|
|
|
|
// signTx,
|
|
|
|
// estimateGas,
|
|
|
|
// getGasPrice,
|
|
|
|
// } = require('./actions')
|
|
|
|
// const { stripHexPrefix, addHexPrefix } = require('ethereumjs-util')
|
|
|
|
// const { isHex, numericBalance, isValidAddress, allNull } = require('./util')
|
|
|
|
// const { conversionUtil, conversionGreaterThan } = require('./conversion-util')
|
|
|
|
|
|
|
|
// module.exports = connect(mapStateToProps)(SendTransactionScreen)
|
|
|
|
|
|
|
|
// function mapStateToProps (state) {
|
|
|
|
// const {
|
|
|
|
// selectedAddress: address,
|
|
|
|
// accounts,
|
|
|
|
// identities,
|
|
|
|
// network,
|
|
|
|
// addressBook,
|
|
|
|
// conversionRate,
|
|
|
|
// currentBlockGasLimit: blockGasLimit,
|
|
|
|
// } = state.metamask
|
|
|
|
// const { warning } = state.appState
|
|
|
|
// const selectedIdentity = getSelectedIdentity(state)
|
|
|
|
// const account = accounts[address]
|
|
|
|
|
|
|
|
// return {
|
|
|
|
// address,
|
|
|
|
// accounts,
|
|
|
|
// identities,
|
|
|
|
// network,
|
|
|
|
// addressBook,
|
|
|
|
// conversionRate,
|
|
|
|
// blockGasLimit,
|
|
|
|
// warning,
|
|
|
|
// selectedIdentity,
|
|
|
|
// error: warning && warning.split('.')[0],
|
|
|
|
// account,
|
|
|
|
// identity: identities[address],
|
|
|
|
// balance: account ? account.balance : null,
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
// inherits(SendTransactionScreen, PersistentForm)
|
|
|
|
// function SendTransactionScreen () {
|
|
|
|
// PersistentForm.call(this)
|
|
|
|
|
|
|
|
// // [WIP] These are the bare minimum of tx props needed to sign a transaction
|
|
|
|
// // We will need a few more for contract-related interactions
|
|
|
|
// this.state = {
|
|
|
|
// newTx: {
|
|
|
|
// from: '',
|
|
|
|
// to: '',
|
|
|
|
// amountToSend: '0x0',
|
|
|
|
// gasPrice: null,
|
|
|
|
// gas: null,
|
|
|
|
// amount: '0x0',
|
|
|
|
// txData: null,
|
|
|
|
// memo: '',
|
|
|
|
// },
|
|
|
|
// activeCurrency: 'USD',
|
|
|
|
// tooltipIsOpen: false,
|
|
|
|
// errors: {},
|
|
|
|
// isValid: false,
|
|
|
|
// }
|
|
|
|
|
|
|
|
// this.back = this.back.bind(this)
|
|
|
|
// this.closeTooltip = this.closeTooltip.bind(this)
|
|
|
|
// this.onSubmit = this.onSubmit.bind(this)
|
|
|
|
// this.setActiveCurrency = this.setActiveCurrency.bind(this)
|
|
|
|
// this.toggleTooltip = this.toggleTooltip.bind(this)
|
|
|
|
// this.validate = this.validate.bind(this)
|
|
|
|
// this.getAmountToSend = this.getAmountToSend.bind(this)
|
|
|
|
// this.setErrorsFor = this.setErrorsFor.bind(this)
|
|
|
|
// this.clearErrorsFor = this.clearErrorsFor.bind(this)
|
|
|
|
|
|
|
|
// this.renderFromInput = this.renderFromInput.bind(this)
|
|
|
|
// this.renderToInput = this.renderToInput.bind(this)
|
|
|
|
// this.renderAmountInput = this.renderAmountInput.bind(this)
|
|
|
|
// this.renderGasInput = this.renderGasInput.bind(this)
|
|
|
|
// this.renderMemoInput = this.renderMemoInput.bind(this)
|
|
|
|
// this.renderErrorMessage = this.renderErrorMessage.bind(this)
|
|
|
|
// }
|
|
|
|
|
|
|
|
// SendTransactionScreen.prototype.componentWillMount = function () {
|
|
|
|
// const { newTx } = this.state
|
|
|
|
// const { address } = this.props
|
|
|
|
|
|
|
|
// Promise.all([
|
|
|
|
// this.props.dispatch(getGasPrice()),
|
|
|
|
// this.props.dispatch(estimateGas({
|
|
|
|
// from: address,
|
|
|
|
// gas: '746a528800',
|
|
|
|
// })),
|
|
|
|
// ])
|
|
|
|
// .then(([blockGasPrice, estimatedGas]) => {
|
|
|
|
// console.log({ blockGasPrice, estimatedGas})
|
|
|
|
// this.setState({
|
|
|
|
// newTx: {
|
|
|
|
// ...newTx,
|
|
|
|
// gasPrice: blockGasPrice,
|
|
|
|
// gas: estimatedGas,
|
|
|
|
// },
|
|
|
|
// })
|
|
|
|
// })
|
|
|
|
// }
|
|
|
|
|
|
|
|
// SendTransactionScreen.prototype.renderErrorMessage = function(errorType, warning) {
|
|
|
|
// const { errors } = this.state
|
|
|
|
// const errorMessage = errors[errorType];
|
|
|
|
|
|
|
|
// return errorMessage || warning
|
|
|
|
// ? h('div.send-screen-input-wrapper__error-message', [ errorMessage || warning ])
|
|
|
|
// : null
|
|
|
|
// }
|
|
|
|
|
|
|
|
// SendTransactionScreen.prototype.renderFromInput = function (from, identities) {
|
|
|
|
// return h('div.send-screen-input-wrapper', [
|
|
|
|
|
|
|
|
// h('div', 'From:'),
|
|
|
|
|
|
|
|
// h('input.large-input.send-screen-input', {
|
|
|
|
// list: 'accounts',
|
|
|
|
// placeholder: 'Account',
|
|
|
|
// value: from,
|
|
|
|
// onChange: (event) => {
|
|
|
|
// this.setState({
|
|
|
|
// newTx: {
|
|
|
|
// ...this.state.newTx,
|
|
|
|
// from: event.target.value,
|
|
|
|
// },
|
|
|
|
// })
|
|
|
|
// },
|
|
|
|
// onBlur: () => this.setErrorsFor('from'),
|
|
|
|
// onFocus: event => {
|
|
|
|
// this.clearErrorsFor('from')
|
|
|
|
// this.state.newTx.from && event.target.select()
|
|
|
|
// },
|
|
|
|
// }),
|
|
|
|
|
|
|
|
// h('datalist#accounts', [
|
|
|
|
// Object.entries(identities).map(([key, { address, name }]) => {
|
|
|
|
// return h('option', {
|
|
|
|
// value: address,
|
|
|
|
// label: name,
|
|
|
|
// key: address,
|
|
|
|
// })
|
|
|
|
// }),
|
|
|
|
// ]),
|
|
|
|
|
|
|
|
// this.renderErrorMessage('from'),
|
|
|
|
|
|
|
|
// ])
|
|
|
|
// }
|
|
|
|
|
|
|
|
// SendTransactionScreen.prototype.renderToInput = function (to, identities, addressBook) {
|
|
|
|
// return h('div.send-screen-input-wrapper', [
|
|
|
|
|
|
|
|
// h('div', 'To:'),
|
|
|
|
|
|
|
|
// h('input.large-input.send-screen-input', {
|
|
|
|
// name: 'address',
|
|
|
|
// list: 'addresses',
|
|
|
|
// placeholder: 'Address',
|
|
|
|
// value: to,
|
|
|
|
// onChange: (event) => {
|
|
|
|
// this.setState({
|
|
|
|
// newTx: {
|
|
|
|
// ...this.state.newTx,
|
|
|
|
// to: event.target.value,
|
|
|
|
// },
|
|
|
|
// })
|
|
|
|
// },
|
|
|
|
// onBlur: () => {
|
|
|
|
// this.setErrorsFor('to')
|
|
|
|
// },
|
|
|
|
// onFocus: event => {
|
|
|
|
// this.clearErrorsFor('to')
|
|
|
|
// this.state.newTx.to && event.target.select()
|
|
|
|
// },
|
|
|
|
// }),
|
|
|
|
|
|
|
|
// h('datalist#addresses', [
|
|
|
|
// // Corresponds to the addresses owned.
|
|
|
|
// ...Object.entries(identities).map(([key, { address, name }]) => {
|
|
|
|
// return h('option', {
|
|
|
|
// value: address,
|
|
|
|
// label: name,
|
|
|
|
// key: address,
|
|
|
|
// })
|
|
|
|
// }),
|
|
|
|
// // Corresponds to previously sent-to addresses.
|
|
|
|
// ...addressBook.map(({ address, name }) => {
|
|
|
|
// return h('option', {
|
|
|
|
// value: address,
|
|
|
|
// label: name,
|
|
|
|
// key: address,
|
|
|
|
// })
|
|
|
|
// }),
|
|
|
|
// ]),
|
|
|
|
|
|
|
|
// this.renderErrorMessage('to'),
|
|
|
|
|
|
|
|
// ])
|
|
|
|
// }
|
|
|
|
|
|
|
|
// SendTransactionScreen.prototype.renderAmountInput = function (activeCurrency) {
|
|
|
|
// return h('div.send-screen-input-wrapper', [
|
|
|
|
|
|
|
|
// h('div.send-screen-amount-labels', [
|
|
|
|
// h('span', 'Amount'),
|
|
|
|
// h(CurrencyToggle, {
|
|
|
|
// activeCurrency,
|
|
|
|
// onClick: (newCurrency) => this.setActiveCurrency(newCurrency),
|
|
|
|
// }), // holding on icon from design
|
|
|
|
// ]),
|
|
|
|
|
|
|
|
// h('input.large-input.send-screen-input', {
|
|
|
|
// placeholder: `0 ${activeCurrency}`,
|
|
|
|
// type: 'number',
|
|
|
|
// onChange: (event) => {
|
|
|
|
// const amountToSend = event.target.value
|
|
|
|
// ? this.getAmountToSend(event.target.value)
|
|
|
|
// : '0x0'
|
|
|
|
|
|
|
|
// this.setState({
|
|
|
|
// newTx: Object.assign(
|
|
|
|
// this.state.newTx,
|
|
|
|
// {
|
|
|
|
// amount: event.target.value,
|
|
|
|
// amountToSend: amountToSend,
|
|
|
|
// }
|
|
|
|
// ),
|
|
|
|
// })
|
|
|
|
// },
|
|
|
|
// onBlur: () => {
|
|
|
|
// this.setErrorsFor('amount')
|
|
|
|
// },
|
|
|
|
// onFocus: () => this.clearErrorsFor('amount'),
|
|
|
|
// }),
|
|
|
|
|
|
|
|
// this.renderErrorMessage('amount'),
|
|
|
|
|
|
|
|
// ])
|
|
|
|
// }
|
|
|
|
|
|
|
|
// SendTransactionScreen.prototype.renderGasInput = function (gasPrice, gas, activeCurrency, conversionRate, blockGasLimit) {
|
|
|
|
// return h('div.send-screen-input-wrapper', [
|
|
|
|
// this.state.tooltipIsOpen && h(GasTooltip, {
|
|
|
|
// className: 'send-tooltip',
|
|
|
|
// gasPrice,
|
|
|
|
// gasLimit: gas,
|
|
|
|
// onClose: this.closeTooltip,
|
|
|
|
// onFeeChange: ({gasLimit, gasPrice}) => {
|
|
|
|
// this.setState({
|
|
|
|
// newTx: {
|
|
|
|
// ...this.state.newTx,
|
|
|
|
// gas: gasLimit,
|
|
|
|
// gasPrice,
|
|
|
|
// },
|
|
|
|
// })
|
|
|
|
// },
|
|
|
|
// }),
|
|
|
|
|
|
|
|
// h('div.send-screen-gas-labels', [
|
|
|
|
// h('span', [
|
|
|
|
// h('i.fa.fa-bolt'),
|
|
|
|
// 'Gas fee:',
|
|
|
|
// ]),
|
|
|
|
// h('span', 'What\'s this?'),
|
|
|
|
// ]),
|
|
|
|
|
|
|
|
// // TODO: handle loading time when switching to USD
|
|
|
|
// h('div.large-input.send-screen-gas-input', {}, [
|
|
|
|
// h(GasFeeDisplay, {
|
|
|
|
// activeCurrency,
|
|
|
|
// conversionRate,
|
|
|
|
// gas,
|
|
|
|
// gasPrice,
|
|
|
|
// blockGasLimit,
|
|
|
|
// }),
|
|
|
|
// h('div.send-screen-gas-input-customize', {
|
|
|
|
// onClick: this.toggleTooltip,
|
|
|
|
// }, [
|
|
|
|
// 'Customize',
|
|
|
|
// ]),
|
|
|
|
// ]),
|
|
|
|
|
|
|
|
// ])
|
|
|
|
// }
|
|
|
|
|
|
|
|
// SendTransactionScreen.prototype.renderMemoInput = function () {
|
|
|
|
// return h('div.send-screen-input-wrapper', [
|
|
|
|
// h('div', 'Transaction memo (optional)'),
|
|
|
|
// h('input.large-input.send-screen-input', {
|
|
|
|
// onChange: () => {
|
|
|
|
// this.setState({
|
|
|
|
// newTx: Object.assign(
|
|
|
|
// this.state.newTx,
|
|
|
|
// {
|
|
|
|
// memo: event.target.value,
|
|
|
|
// }
|
|
|
|
// ),
|
|
|
|
// })
|
|
|
|
// },
|
|
|
|
// }),
|
|
|
|
// ])
|
|
|
|
// }
|
|
|
|
|
|
|
|
// SendTransactionScreen.prototype.render = function () {
|
|
|
|
// this.persistentFormParentId = 'send-tx-form'
|
|
|
|
|
|
|
|
// const props = this.props
|
|
|
|
// const {
|
|
|
|
// warning,
|
|
|
|
// identities,
|
|
|
|
// addressBook,
|
|
|
|
// conversionRate,
|
|
|
|
// } = props
|
|
|
|
|
|
|
|
// const {
|
|
|
|
// blockGasLimit,
|
|
|
|
// newTx,
|
|
|
|
// activeCurrency,
|
|
|
|
// isValid,
|
|
|
|
// } = this.state
|
|
|
|
// const { gas, gasPrice } = newTx
|
|
|
|
|
|
|
|
// return (
|
|
|
|
|
|
|
|
// h('div.send-screen-wrapper', [
|
|
|
|
// // Main Send token Card
|
|
|
|
// h('div.send-screen-card', [
|
|
|
|
|
|
|
|
// h('img.send-eth-icon', { src: '../images/eth_logo.svg' }),
|
|
|
|
|
|
|
|
// h('div.send-screen__title', 'Send'),
|
|
|
|
|
|
|
|
// h('div.send-screen__subtitle', 'Send Ethereum to anyone with an Ethereum account'),
|
|
|
|
|
|
|
|
// this.renderFromInput(this.state.newTx.from, identities),
|
|
|
|
|
|
|
|
// this.renderToInput(this.state.newTx.to, identities, addressBook),
|
|
|
|
|
|
|
|
// this.renderAmountInput(activeCurrency),
|
|
|
|
|
|
|
|
// this.renderGasInput(
|
|
|
|
// gasPrice || '0x0',
|
|
|
|
// gas || '0x0',
|
|
|
|
// activeCurrency,
|
|
|
|
// conversionRate,
|
|
|
|
// blockGasLimit
|
|
|
|
// ),
|
|
|
|
|
|
|
|
// this.renderMemoInput(),
|
|
|
|
|
|
|
|
// this.renderErrorMessage(null, warning),
|
|
|
|
|
|
|
|
// ]),
|
|
|
|
|
|
|
|
// // Buttons underneath card
|
|
|
|
// h('section.flex-column.flex-center', [
|
|
|
|
// h('button.btn-secondary.send-screen__send-button', {
|
|
|
|
// className: !isValid && 'send-screen__send-button__disabled',
|
|
|
|
// onClick: (event) => isValid && this.onSubmit(event),
|
|
|
|
// }, 'Next'),
|
|
|
|
// h('button.btn-tertiary.send-screen__cancel-button', {
|
|
|
|
// onClick: this.back,
|
|
|
|
// }, 'Cancel'),
|
|
|
|
// ]),
|
|
|
|
// ])
|
|
|
|
|
|
|
|
// )
|
|
|
|
// }
|
|
|
|
|
|
|
|
// SendTransactionScreen.prototype.toggleTooltip = function () {
|
|
|
|
// this.setState({ tooltipIsOpen: !this.state.tooltipIsOpen })
|
|
|
|
// }
|
|
|
|
|
|
|
|
// SendTransactionScreen.prototype.closeTooltip = function () {
|
|
|
|
// this.setState({ tooltipIsOpen: false })
|
|
|
|
// }
|
|
|
|
|
|
|
|
// SendTransactionScreen.prototype.setActiveCurrency = function (newCurrency) {
|
|
|
|
// this.setState({ activeCurrency: newCurrency })
|
|
|
|
// }
|
|
|
|
|
|
|
|
// SendTransactionScreen.prototype.back = function () {
|
|
|
|
// var address = this.props.address
|
|
|
|
// this.props.dispatch(backToAccountDetail(address))
|
|
|
|
// }
|
|
|
|
|
|
|
|
// SendTransactionScreen.prototype.validate = function (balance, amountToSend, { to, from }) {
|
|
|
|
// const sufficientBalance = conversionGreaterThan(
|
|
|
|
// {
|
|
|
|
// value: balance,
|
|
|
|
// fromNumericBase: 'hex',
|
|
|
|
// },
|
|
|
|
// {
|
|
|
|
// value: amountToSend,
|
|
|
|
// fromNumericBase: 'hex',
|
|
|
|
// },
|
|
|
|
// )
|
|
|
|
|
|
|
|
// const amountLessThanZero = conversionGreaterThan(
|
|
|
|
// {
|
|
|
|
// value: 0,
|
|
|
|
// fromNumericBase: 'dec',
|
|
|
|
// },
|
|
|
|
// {
|
|
|
|
// value: amountToSend,
|
|
|
|
// fromNumericBase: 'hex',
|
|
|
|
// },
|
|
|
|
// )
|
|
|
|
|
|
|
|
// const errors = {}
|
|
|
|
|
|
|
|
// if (!sufficientBalance) {
|
|
|
|
// errors.amount = 'Insufficient funds.'
|
|
|
|
// }
|
|
|
|
|
|
|
|
// if (amountLessThanZero) {
|
|
|
|
// errors.amount = 'Can not send negative amounts of ETH.'
|
|
|
|
// }
|
|
|
|
|
|
|
|
// if (!from) {
|
|
|
|
// errors.from = 'Required'
|
|
|
|
// }
|
|
|
|
|
|
|
|
// if (from && !isValidAddress(from)) {
|
|
|
|
// errors.from = 'Sender address is invalid.'
|
|
|
|
// }
|
|
|
|
|
|
|
|
// if (!to) {
|
|
|
|
// errors.to = 'Required'
|
|
|
|
// }
|
|
|
|
|
|
|
|
// if (to && !isValidAddress(to)) {
|
|
|
|
// errors.to = 'Recipient address is invalid.'
|
|
|
|
// }
|
|
|
|
|
|
|
|
// // if (txData && !isHex(stripHexPrefix(txData))) {
|
|
|
|
// // message = 'Transaction data must be hex string.'
|
|
|
|
// // return this.props.dispatch(displayWarning(message))
|
|
|
|
// // }
|
|
|
|
|
|
|
|
// return {
|
|
|
|
// isValid: allNull(errors),
|
|
|
|
// errors,
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
// SendTransactionScreen.prototype.getAmountToSend = function (amount) {
|
|
|
|
// const { activeCurrency } = this.state
|
|
|
|
// const { conversionRate } = this.props
|
|
|
|
|
|
|
|
// return conversionUtil(amount, {
|
|
|
|
// fromNumericBase: 'dec',
|
|
|
|
// toNumericBase: 'hex',
|
|
|
|
// fromCurrency: activeCurrency,
|
|
|
|
// toCurrency: 'ETH',
|
|
|
|
// toDenomination: 'WEI',
|
|
|
|
// conversionRate,
|
|
|
|
// invertConversionRate: activeCurrency !== 'ETH',
|
|
|
|
// })
|
|
|
|
// }
|
|
|
|
|
|
|
|
// SendTransactionScreen.prototype.setErrorsFor = function (field) {
|
|
|
|
// const { balance } = this.props
|
|
|
|
// const { newTx, errors: previousErrors } = this.state
|
|
|
|
// const { amountToSend } = newTx
|
|
|
|
|
|
|
|
// const {
|
|
|
|
// isValid,
|
|
|
|
// errors: newErrors
|
|
|
|
// } = this.validate(balance, amountToSend, newTx)
|
|
|
|
|
|
|
|
// const nextErrors = Object.assign({}, previousErrors, {
|
|
|
|
// [field]: newErrors[field] || null
|
|
|
|
// })
|
|
|
|
|
|
|
|
// if (!isValid) {
|
|
|
|
// this.setState({
|
|
|
|
// errors: nextErrors,
|
|
|
|
// isValid,
|
|
|
|
// })
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
// SendTransactionScreen.prototype.clearErrorsFor = function (field) {
|
|
|
|
// const { errors: previousErrors } = this.state
|
|
|
|
// const nextErrors = Object.assign({}, previousErrors, {
|
|
|
|
// [field]: null
|
|
|
|
// })
|
|
|
|
|
|
|
|
// this.setState({
|
|
|
|
// errors: nextErrors,
|
|
|
|
// isValid: allNull(nextErrors),
|
|
|
|
// })
|
|
|
|
// }
|
|
|
|
|
|
|
|
// SendTransactionScreen.prototype.onSubmit = function (event) {
|
|
|
|
// event.preventDefault()
|
|
|
|
// const { warning, balance } = this.props
|
|
|
|
// const state = this.state || {}
|
|
|
|
|
|
|
|
// const recipient = state.newTx.to
|
|
|
|
// const sender = state.newTx.from
|
|
|
|
// const nickname = state.nickname || ' '
|
|
|
|
|
|
|
|
// // TODO: convert this to hex when created and include it in send
|
|
|
|
// const txData = state.newTx.memo
|
|
|
|
|
|
|
|
// this.props.dispatch(hideWarning())
|
|
|
|
|
|
|
|
// this.props.dispatch(addToAddressBook(recipient, nickname))
|
|
|
|
|
|
|
|
// var txParams = {
|
|
|
|
// from: this.state.newTx.from,
|
|
|
|
// to: this.state.newTx.to,
|
|
|
|
|
|
|
|
// value: this.state.newTx.amountToSend,
|
|
|
|
|
|
|
|
// gas: this.state.newTx.gas,
|
|
|
|
// gasPrice: this.state.newTx.gasPrice,
|
|
|
|
// }
|
|
|
|
|
|
|
|
// if (recipient) txParams.to = addHexPrefix(recipient)
|
|
|
|
// if (txData) txParams.data = txData
|
|
|
|
|
|
|
|
// this.props.dispatch(signTx(txParams))
|
|
|
|
// }
|